package com.ccys.doctor.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.ccys.common.constants.CoreAppConstants;
import com.ccys.common.util.Kit;
import com.ccys.common.vo.ResponseResultApp;
import com.ccys.db.cache.SysCodeUtil;
import com.ccys.db.cache.UserUtil;
import com.ccys.db.dto.doctor.completedOrEndCall.*;
import com.ccys.db.entity.*;
import com.ccys.db.mapper.*;
import com.ccys.doctor.service.CompletedOrEndCallService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: WhiteCoat
 * @description: CompletedOrEndCallService
 * @author: ZhangJingCan
 * @create: 2024-03-04 11:58
 **/

@Service
public class CompletedOrEndCallServiceImpl implements CompletedOrEndCallService {

    @Resource
    private TblBookingMapper tblBookingMapper;

    @Resource
    private TblProductMapper tblProductMapper;

    @Resource
    private TblDoctorMapper tblDoctorMapper;

    @Resource
    private TblBookingMedicationsMapper tblBookingMedicationsMapper;

    @Resource
    private TblBookingAllergiesMapper tblBookingAllergiesMapper;

    @Resource
    private TblMemberAllergiesMapper tblMemberAllergiesMapper;

    @Resource
    private TblMedicationReactionMapper tblMedicationReactionMapper;

    @Resource
    private TblBookingSummaryMapper tblBookingSummaryMapper;

    @Resource
    private TblSymptomMapper tblSymptomMapper;

    @Resource
    private TblBookingSymptomMapper tblBookingSymptomMapper;

    @Resource
    private TblBookingConsultationMapper tblBookingConsultationMapper;

    @Resource
    private TblBookingFeeMapper tblBookingFeeMapper;

    @Resource
    private TblBookingQueueMapper tblBookingQueueMapper;

    @Resource
    private TblBookingPrescriptionMapper tblBookingPrescriptionMapper;

    @Resource
    private TblBookingOrderMapper tblBookingOrderMapper;

    @Resource
    private TblMemberMapper tblMemberMapper;

    @Resource
    private TblMemberSummaryMapper tblMemberSummaryMapper;

    @Resource
    private TblBookingLogMapper tblBookingLogMapper;

    @Resource
    private TblDiagnosisMapper tblDiagnosisMapper;

    @Resource
    private TblBookingDiagnosisMapper tblBookingDiagnosisMapper;

    @Resource
    private TblBookingReferralLetterMapper tblBookingReferralLetterMapper;

    @Resource
    private TblBookingMemberInfoMapper tblBookingMemberInfoMapper;

    @Resource
    private TblFlagMemberMapper tblFlagMemberMapper;

    @Resource
    private TblBookingMedicalServiceMapper tblBookingMedicalServiceMapper;

    @Resource
    private TblBookingTextBasedMapper tblBookingTextBasedMapper;

    @Resource
    private TblBookingEapMapper tblBookingEapMapper;

    @Resource
    private TblMedicalServiceMapper tblMedicalServiceMapper;

    @Resource
    private TblBookingPackageMapper tblBookingPackageMapper;

    @Resource
    private TblPackageMapper tblPackageMapper;

    @Resource
    private TblDeeplinkMapper tblDeeplinkMapper;

    @Resource
    private TblInventoryFeedMapper tblInventoryFeedMapper;

    @Resource
    private TblDoctorConsultFeeMapper tblDoctorConsultFeeMapper;

    @Resource
    private SysCodeMapper sysCodeMapper;

    @Resource
    private TblMedicalServiceTypeMapper tblMedicalServiceTypeMapper;

    @Resource
    private TblPlanBenefitMapper tblPlanBenefitMapper;

    @Resource
    private TblBookingPatientAddressMapper tblBookingPatientAddressMapper;

    @Resource
    private TblClinicMapper tblClinicMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp completeBooking(CompleteBookingDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return ResponseResultApp.error("Unauthorised request.");
        }

        if (ObjectUtil.isEmpty(dto.getDiagnosis())) {
            return ResponseResultApp.error("There is no Diagnosis added yet. \n" +
                    "Please check and add a Diagnosis.");
        }
        dto.setSpecialist_id((dto.getSpecialist_id() != null) ? dto.getSpecialist_id() : 0);

        if (dto.getConsult_fee_id().equals("0")) {
            return ResponseResultApp.error("Please select a consultation charges.");
        }
        if (dto.getPrescriptions().isEmpty()) {
            return ResponseResultApp.error("Prescription list must be at least 1 medication");
        }
        dto.setFlag_patient_id(dto.getFlag_patient_id() == null ? 0 : dto.getFlag_patient_id());
        dto.setMedical_letter(dto.getMedical_letter() != null ? dto.getMedical_letter() : "");
        dto.setFlag_patient_id(dto.getFlag_patient_id() != null ? dto.getFlag_patient_id() : 0);
        if (ObjectUtil.isEmpty(dto.getMedical_certification_begin_date()) || ObjectUtil.isEmpty(dto.getMedical_certification_end_date())) {
            // 创建 Calendar 对象
            Calendar calendar = Calendar.getInstance();
            // 设置日期为1900年1月1日
            calendar.set(Calendar.YEAR, 1900); // 年
            calendar.set(Calendar.MONTH, Calendar.JANUARY); // 月份从0开始计数，0表示1月
            calendar.set(Calendar.DAY_OF_MONTH, 1); // 日
            // 获取 Date 对象
            Date date = calendar.getTime();
            if (ObjectUtil.isEmpty(dto.getMedical_certification_begin_date())) {
                dto.setMedical_certification_begin_date(date);
            }
            if (ObjectUtil.isEmpty(dto.getMedical_certification_end_date())) {
                dto.setMedical_certification_end_date(date);
            }
        }

        if (dto.getFlag_patient_id() < 0) {
            return ResponseResultApp.error("The reason id must be larger than 0.");
        }
        if (!"1900/01/01".equals(Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd")) &&
                !"1900/01/01".equals(Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd")) &&
                Kit.calculateDateDifferenceInDays(dto.getMedical_certification_end_date(), dto.getMedical_certification_begin_date()) < 0) {
            return ResponseResultApp.error("The medical certification begin date must be earlier than medical certification end date");
        } else {
            for (CompleteBookingDto.Prescriptions prescription : dto.getPrescriptions()) {
                if (prescription.getQuantity().compareTo(BigDecimal.ONE) < 0) {
                    return ResponseResultApp.error("Quantity must be larger than 0.");
                }
            }
        }
        dto.setMedical_certification_additional_note(dto.getMedical_certification_additional_note() != null ? dto.getMedical_certification_additional_note() : "");

        for (CompleteBookingDto.Prescriptions prescription : dto.getPrescriptions()) {
            if (prescription.getRefill() > 0 && ObjectUtil.isEmpty(prescription.getRefill_expiry_date())) {
                return ResponseResultApp.error("Refill expiry date required.");
            } else if (prescription.getRefill() > 0 && ObjectUtil.isNotEmpty(prescription.getRefill_expiry_date()) &&
                    Kit.calculateDateDifferenceInDays(Objects.requireNonNull(Kit.parseDate(prescription.getRefill_expiry_date(), "yyyy/MM/dd")), new Date()) < 0) {
                return ResponseResultApp.error("Refill expiry date must equal or greater than today.");
            }
        }

        String memberId = booking.getMemId();
        if (2 == booking.getMemType()) {
            //小孩
            memberId = booking.getPatientMemId();
        }
        //Insert tbl_Booking_Medications
        /**
         * todo 此处省略了两个sql - tbl_MD_Medication既然传入了product并且有id，那还存一遍干啥，所以不操作, tbl_MD_Allergies是因为没有表
         * 	INSERT  INTO tbl_MD_Medication (MDMedication_ID,MDMedication_name,MDMedication_description,MDMedication_is_show, MDMedication_status) SELECT medication_id,name,N'free text allergy',1, 1 from @PRM_medications  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_Medication f WHERE cr.medication_id = f.MDMedication_ID)
         * 	INSERT  INTO tbl_MD_Allergies (MDAllergies_ID,MDAllergies_name,MDAllergies_description,MDAllergies_is_show, MDAllergies_status) SELECT medication_id,name,N'free text allergy', 1, 1 from @PRM_medications  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_Allergies f WHERE cr.medication_id = f.MDAllergies_ID)
         */
        tblBookingMedicationsMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getMedications())) {
            for (CompleteBookingDto.Medications medication : dto.getMedications()) {
                medication.setUsage_period(medication.getUsage_period() != null ? medication.getUsage_period() : "");
                medication.setAmount_of_dosage(medication.getAmount_of_dosage() != null ? medication.getAmount_of_dosage() : "");
                TblBookingMedications bookingMedications = new TblBookingMedications();
                bookingMedications.setBookingId(booking.getId());
                bookingMedications.setProductId(medication.getId());
                bookingMedications.setMedicationUsagePeriodId(ObjectUtil.isEmpty(medication.getUsage_period()) ? null : medication.getUsage_period());
                bookingMedications.setAmountOfDosage(medication.getAmount_of_dosage() != null ? medication.getAmount_of_dosage() : null);
                tblBookingMedicationsMapper.insertSelective(bookingMedications);
            }
        }

        //Insert tbl_Booking_Allergies
        /**
         * todo 此处省略了两个sql - 和上方一样
         * 	INSERT  INTO tbl_MD_Medication (MDMedication_ID,MDMedication_name,MDMedication_description,MDMedication_is_show, MDMedication_status) SELECT [allergy_id],name,N'free text allergy', 1, 1 from @PRM_allergies  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_Medication f WHERE cr.[allergy_id] = f.MDMedication_ID)
         * 	INSERT  INTO tbl_MD_Allergies (MDAllergies_ID,MDAllergies_name,MDAllergies_description,MDAllergies_is_show, MDAllergies_status) SELECT [allergy_id],name,N'free text allergy', 1, 1 from @PRM_allergies  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_Allergies f WHERE cr.[allergy_id] = f.MDAllergies_ID)
         */
        tblBookingAllergiesMapper.updateDelFlagTrueByBookingId(booking.getId());
        //Insert tbl_Member_Allergies
        tblMemberAllergiesMapper.updateDelFlagTrueByMemberId(memberId);
        if (ObjectUtil.isNotEmpty(dto.getAllergies())) {
            for (CompleteBookingDto.Allergies allergy : dto.getAllergies()) {
                TblProduct byId = tblProductMapper.findById(allergy.getAllergy_id());
                if (ObjectUtil.isNotEmpty(byId)) {
                    //这个属于Insert tbl_Booking_Allergies
                    TblBookingAllergies bookingAllergies = new TblBookingAllergies();
                    bookingAllergies.setBookingId(booking.getId());
                    bookingAllergies.setAllergiesName(byId.getName());
                    tblBookingAllergiesMapper.insertSelective(bookingAllergies);

                    //这个属于Insert tbl_Member_Allergies
                    TblMemberAllergies memberAllergies = new TblMemberAllergies();
                    memberAllergies.setMemId(memberId);
                    memberAllergies.setAllergiesName(byId.getName());
                    tblMemberAllergiesMapper.insertSelective(memberAllergies);
                }
            }
        }

        //Insert tbl_Booking_MedicationReaction
        /**
         * todo 此处省略的sql 这个新增sql的条件是MD_MedicationReaction_ID不存在于数据库然后添加，既然MD_MedicationReaction_ID都传过来了说明前端拿到了值了，咋可能不存在？
         * 	INSERT  INTO tbl_MD_MedicationReaction (MD_MedicationReaction_ID,MD_MedicationReaction_name,MD_MedicationReaction_is_show, MD_MedicationReaction_status) SELECT [medication_reaction_id],name, 1, 1 from @PRM_medication_reactions  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_MedicationReaction f WHERE cr.[medication_reaction_id] = f.MD_MedicationReaction_ID)
         */
        /**
         * 此处省略
         * 	delete tbl_Booking_MedicationReaction where  booking_id=@booking_id
         * 	原因：tbl_Booking_MedicationReaction整合至tbl_booking_allergies 上方已经根据bookingId软删除了一次，所以这里不需要再次删除
         * 	只管新增即可
         */
        if (ObjectUtil.isNotEmpty(dto.getMedication_reactions())) {
            for (CompleteBookingDto.MedicationReactions reaction : dto.getMedication_reactions()) {
                TblMedicationReaction byId = tblMedicationReactionMapper.findById(reaction.getMedication_reaction_id());
                if (ObjectUtil.isNotEmpty(byId)) {
                    TblBookingAllergies bookingAllergies = new TblBookingAllergies();
                    bookingAllergies.setBookingId(booking.getId());
                    bookingAllergies.setMedicationReactionId(reaction.getMedication_reaction_id());
                    //对方还添加了otherreaction字段，但是我们这个表没有 - 忽略
                    tblBookingAllergiesMapper.insertSelective(bookingAllergies);

                    /**
                     * 省略Delete tbl_Member_MedicationReactions Where mem_id = @PRM_member_id
                     * 和上方省略一样 因为已经删除了一遍 - 只管新增即可
                     */
                    TblMemberAllergies memberAllergies = new TblMemberAllergies();
                    memberAllergies.setMemId(memberId);
                    memberAllergies.setMedicationReactionId(reaction.getMedication_reaction_id());
                    //对方还添加了otherreaction字段，但是我们这个表没有 - 忽略
                    tblMemberAllergiesMapper.insertSelective(memberAllergies);
                }
            }
        }
        if (ObjectUtil.isNotEmpty(booking.getDoctorIsCompleted()) && 1 == booking.getDoctorIsCompleted()) {
            return ResponseResultApp.error("Sorry, this booking has been completed.");
        }

        TblBookingSummary bookingSummary = tblBookingSummaryMapper.findByBookingId(booking.getId());
        if (1 == bookingSummary.getNeedMedicalLetter() && dto.getReferrals().isEmpty()) {
            return ResponseResultApp.error("The medical letter field is required.");
        } else if (1 == bookingSummary.getNeedMedicalCertification() && Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")) {
            return ResponseResultApp.error("The medical certification begin date field is required.");
        } else if (1 == bookingSummary.getNeedMedicalCertification() && Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
            return ResponseResultApp.error("The medical certification end date field is required.");
        }

        tblBookingSymptomMapper.updateDelFlagTrueByBookingId(booking.getId());

        if (ObjectUtil.isNotEmpty(dto.getSymptoms())) {
            //查询当前集团的最大order
            Integer order = tblSymptomMapper.findOrderByGroupId(doctor.getGroupId());
            for (CompleteBookingDto.Symptoms symptom : dto.getSymptoms()) {
                TblSymptom check = tblSymptomMapper.findByNameAndByGroupId(symptom.getName(), doctor.getGroupId());
                if (ObjectUtil.isEmpty(check)) {
                    TblSymptom tblSymptom = new TblSymptom();
                    tblSymptom.setName(symptom.getName());
                    tblSymptom.setDispOrder(order);
                    order++;
                    tblSymptomMapper.insertSelective(tblSymptom);
                }
                //保存bookingSymptom
                TblSymptom db = tblSymptomMapper.findByNameAndByGroupId(symptom.getName(), doctor.getGroupId());
                TblBookingSymptom bookingSymptom = new TblBookingSymptom();
                bookingSymptom.setBookingId(booking.getId());
                bookingSymptom.setSymptomId(db.getId());
                tblBookingSymptomMapper.insertSelective(bookingSymptom);
            }
        }

        //Update tbl_Booking_Consultation
        TblBookingConsultation bookingConsultation = tblBookingConsultationMapper.findByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(bookingConsultation)) {
            // 计算时间差（以毫秒为单位）
            long timeDifference = new Date().getTime() - bookingConsultation.getBeginTimeUtc().getTime();
            // 计算时间差的小时、分钟和秒
            long hours = timeDifference / (60 * 60 * 1000);
            long minutes = (timeDifference % (60 * 60 * 1000)) / (60 * 1000);
            long seconds = ((timeDifference % (60 * 60 * 1000)) % (60 * 1000)) / 1000;
            // 格式化输出时间差
            String timeDifferenceStr = String.format("%02d:%02d:%02d", hours, minutes, seconds);
            bookingConsultation.setDuration(timeDifferenceStr);
            bookingConsultation.setStatus(1);
            tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
        }
        TblBookingFee bookingFee = tblBookingFeeMapper.findByBookingId(booking.getId());
        //Update tbl_Booking Of Patient Current
        bookingSummary.setPresentingComplaint(dto.getPresenting_complaint() != null ? dto.getPresenting_complaint() : null);
        bookingSummary.setManagement(dto.getMamagement() != null ? dto.getMamagement() : null);
        bookingSummary.setInstructionsToPatient(dto.getInstructions_to_patient() != null ? dto.getInstructions_to_patient() : null);
        bookingSummary.setPatientWeight(dto.getPatient_weight() != null ? dto.getPatient_weight() : null);
        bookingSummary.setPatientHeight(dto.getPatient_height() != null ? dto.getPatient_height() : null);
        bookingSummary.setPatientTemp(dto.getPatient_temp() != null ? dto.getPatient_temp() : null);
        bookingSummary.setPatientBp1(dto.getPatient_bp1() != null ? dto.getPatient_bp1() : null);
        bookingSummary.setPatientBp2(dto.getPatient_bp2() != null ? dto.getPatient_bp2() : null);
        bookingSummary.setPatientHr(dto.getPatient_hr() != null ? dto.getPatient_hr() : null);
        bookingSummary.setBasicCdmpInformation(dto.getBasic_cdmp_information() != null ? dto.getBasic_cdmp_information() : null);
        booking.setIsCdmpChronic(dto.getIs_cdmp_chronic() != null ? dto.getIs_cdmp_chronic() ? 1 : 0 : null);
        booking.setFollowUpConsultationNo(dto.getFollow_up_code() != null ? dto.getFollow_up_code() : null);
        bookingFee.setConsultFeeId(dto.getConsult_fee_id() != null ? dto.getConsult_fee_id() : null);
        bookingFee.setConsultFeeText(dto.getConsult_fee_text() != null ? dto.getConsult_fee_text() : null);
        if (ObjectUtil.isNotEmpty(bookingSummary.getNeedMedicalCertification()) && 0 != bookingSummary.getNeedMedicalCertification()) {
            if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")
                    && !Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationCode("MC" + booking.getConsultationNo());
                bookingSummary.setAdditionalNote(dto.getMedical_certification_additional_note() != null ? dto.getMedical_certification_additional_note() : null);
            }
            if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationBeginDate(dto.getMedical_certification_begin_date());
            }
            if (!Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationEndDate(dto.getMedical_certification_end_date());
            }
        }
        tblBookingQueueMapper.updateQueueNoSetNullByBookingId(booking.getId());
        booking.setDoctorIsCompleted(1);
        bookingSummary.setSummary(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
        bookingSummary.setDiagnosisAllowPatientToClaim(dto.getDiagnosis_allow_patient_to_claim() != null ? dto.getDiagnosis_allow_patient_to_claim() ? 1 : 0 : null);
        bookingSummary.setCountryRemark(dto.getCountry_remark() != null ? dto.getCountry_remark() : null);
        if (-1 != booking.getStatus() && booking.getStatus() < 4) {
            booking.setStatus(4);
        }
        tblBookingMapper.updateByPrimaryKeySelective(booking);
        tblBookingSummaryMapper.updateByPrimaryKeySelective(bookingSummary);
        tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);

        /**
         * 省略sql - 原因：未找到对应表
         * 			Exec dbo.API_Update_Booking_For_Specialist
         *                                          @PRM_booking_id,
         *                     @PRM_progression_of_symptoms,
         *                     @PRM_frequency_of_symptoms
         */

        //Update tbl_member patient vitals and summary
        TblMember member = tblMemberMapper.findById(memberId);
        TblMemberSummary memberSummary = tblMemberSummaryMapper.findOneByBookingId(booking.getId());
        if (ObjectUtil.isEmpty(memberSummary)) {
            memberSummary = new TblMemberSummary();
            memberSummary.setMemberId(memberId);
            memberSummary.setBody(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
            memberSummary.setDoctorId(doctor.getId());
            tblMemberSummaryMapper.insertSelective(memberSummary);
        } else {
            memberSummary.setMemberId(memberId);
            memberSummary.setBody(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
            memberSummary.setDoctorId(doctor.getId());
            tblMemberSummaryMapper.updateByPrimaryKeySelective(memberSummary);
        }

        //Update patient’s measurement
        if (2 == booking.getMemType()) {
            //todo exec [dbo].[spCheckConsultAndLabResult] @PRM_Mem_Id=@child_id,@PRM_Booking_Id=@PRM_booking_id,@PRM_Doctor_Id=@PRM_doctor_id,@PRM_Weight=@PRM_patient_weight,@PRM_Height=@PRM_patient_height
            spCheckConsultAndLabResult(member, booking, doctor);
        } else {
            //todo [dbo].[spCheckConsultAndLabResult] @PRM_Mem_Id=@mem_id,@PRM_Booking_Id=@PRM_booking_id,@PRM_Doctor_Id=@PRM_doctor_id,@PRM_Weight=@PRM_patient_weight,@PRM_Height=@PRM_patient_height
        }

        //Update Diagnosis
        tblBookingDiagnosisMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getDiagnosis())) {
            for (CompleteBookingDto.Diagnosis diagnosis : dto.getDiagnosis()) {
                if (ObjectUtil.isNotEmpty(diagnosis.getName())) {
                    //不存在才添加
                    if (ObjectUtil.isEmpty(tblDiagnosisMapper.findByGroupIdAndByNameOrCode(doctor.getGroupId(), diagnosis.getName()))) {
                        TblDiagnosis tblDiagnosis = new TblDiagnosis();
                        tblDiagnosis.setGroupId(doctor.getGroupId());
                        tblDiagnosis.setName(diagnosis.getName());
                        tblDiagnosis.setDispOrder(diagnosis.getOrdering() != null ? diagnosis.getOrdering() : null);
                        tblDiagnosis.setCountryOfResidence(booking.getDoctorCountryOfResidence());
                        tblDiagnosisMapper.insertSelective(tblDiagnosis);
                    }
                    TblDiagnosis findDiagnosis = tblDiagnosisMapper.findByNameAndByGroupIdAndByDoctorCountryId(diagnosis.getName(), doctor.getGroupId(), booking.getDoctorCountryOfResidence());
                    if (ObjectUtil.isNotEmpty(findDiagnosis)) {
                        TblBookingDiagnosis bookingDiagnosis = new TblBookingDiagnosis();
                        bookingDiagnosis.setBookingId(booking.getId());
                        bookingDiagnosis.setDiagnosisId(findDiagnosis.getId());
                        //这个表对方还设置了is_in_exclusion_list 但是我们TblBookingDiagnosis和TblDiagnosis都没有所以忽略
                        tblBookingDiagnosisMapper.insertSelective(bookingDiagnosis);
                    }
                }
            }
        }

        /**
         * todo 这里少了一个sql：
         * --WC-3272
         * 	DECLARE @json NVARCHAR(MAX)
         * 	SET @json=(SELECT * FROM @PRM_diagnosis
         * 		FOR JSON AUTO)
         * 	EXEC API_Doctor_UpdateDiagnosisLog @PRM_booking_id,@json,0
         */

        //Update Referral Letter
        tblBookingReferralLetterMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getReferrals())) {
            for (CompleteBookingDto.Referrals referral : dto.getReferrals()) {
                TblBookingReferralLetter bookingReferralLetter = new TblBookingReferralLetter();
                bookingReferralLetter.setBookingId(booking.getId());
                bookingReferralLetter.setSpecialistId(referral.getSpecialist_id() != null ? referral.getSpecialist_id() : null);
                bookingReferralLetter.setSpecialisationId(referral.getMDST_ID() != null ? referral.getMDST_ID() : null);
                bookingReferralLetter.setReferralLetterText(referral.getReferral_letter_text() != null ? referral.getReferral_letter_text() : null);
                bookingReferralLetter.setReferralLetterCode(referral.getReferral_letter_code() != null ? "ML" + booking.getConsultationNo() + referral.getReferral_letter_code() : null);
                tblBookingReferralLetterMapper.insertSelective(bookingReferralLetter);
            }
        }

        //delete queue
        tblBookingQueueMapper.deleteByBookingId(booking.getId());

        //ADD LOG QUEUE : booking_id - action - sub_action - driver - date_time - doctor_id
        //todo EXEC [dbo].[API_Insert_Booking_Log] @PRM_booking_id, 100, 6, '', null, null

        if (0 == CoreAppConstants.IS_BOOKING_QUEUE_ENABLED) {
            /**
             *         IF @PRM_Is_Implement_PTMQ = 0 --WC-3201
             *         BEGIN
             *             -- Reset Queue for patient
             *             IF dbo.fn_check_SP_Doctor(@PRM_doctor_id) = 0
             *             BEGIN
             *                 exec dbo.Reset_Num_Patient_InFrontOf
             *             END
             *             ELSE
             *             BEGIN
             *                 exec dbo.Reset_Num_Patient_InFrontOf_SP @PRM_doctor_id
             *             END
             *         END
             */
            if (2 == doctor.getPanelType()) {
                //GP

            } else {
                //SP
            }
        }


        if (5 == booking.getConsultationMode()) {
            //Exec [dbo].[sp_UpdateBookingStatus] @PRM_booking_id, 99, 1
            booking.setStatus(99);
            booking.setSubStatus(1);
            tblBookingMapper.updateByPrimaryKeySelective(booking);
            //lock patient info
            if (ObjectUtil.isEmpty(tblBookingMemberInfoMapper.findByBookingId(booking.getId()))) {
                TblBookingMemberInfo bookingMemberInfo = new TblBookingMemberInfo();
                TblMember mem = null;
                TblMember child = null;
                if (1 == booking.getConsultationMode()) {
                    mem = member;
                } else {
                    child = member;
                    mem = tblMemberMapper.findById(booking.getMemId());
                }
                bookingMemberInfo.setBookingId(booking.getId());
                if (ObjectUtil.isNotEmpty(mem)) {
                    bookingMemberInfo.setMemberId(member.getId());
                    bookingMemberInfo.setMemberDob(member.getDob());
                    bookingMemberInfo.setMemberEmail(member.getEmail());
                    bookingMemberInfo.setMemberFirstName(member.getFirstName());
                    bookingMemberInfo.setMemberLastName(member.getLastName() != null ? member.getLastName() : null);
                    bookingMemberInfo.setMemberGender(member.getGender());
                    bookingMemberInfo.setMemberNricFin(member.getNricFin());
                    bookingMemberInfo.setMemberPhoneNumber(member.getPhone());
                }
                if (ObjectUtil.isNotEmpty(child)) {
                    bookingMemberInfo.setChildId(child.getId());
                    bookingMemberInfo.setChildDob(child.getDob());
                    bookingMemberInfo.setChildEmail(child.getEmail());
                    bookingMemberInfo.setChildFirstName(child.getFirstName());
                    bookingMemberInfo.setChildLastName(child.getLastName() != null ? member.getLastName() : null);
                    bookingMemberInfo.setChildGender(child.getGender());
                    bookingMemberInfo.setChildNricFin(child.getNricFin());
                    bookingMemberInfo.setChildPhoneNumber(child.getPhone());
                }
                tblBookingMemberInfoMapper.insertSelective(bookingMemberInfo);

                //Caclulation total cost of consulation
                //todo 该做函数：API_DoctorCompleteBooking 的 Begin --- Caclulation total cost of consulation ---
                // todo 以下是剩余函数
//                Begin --- Caclulation total cost of consulation ---
//                        Select @booking_total_cost_consulation = SUM(consulation_amount) From tbl_Booking_Consultation WITH(NOLOCK) Where booking_id = @PRM_booking_id
//                        Set @booking_total_cost_consulation = [dbo].[fn_FormatRoundNumber](@booking_total_cost_consulation)
//                        End
//                Begin --- Caclulation total product package fee ---
//                        Select @product_package_fee = (Select SUM(price * quantity) From tbl_Booking_ProductPackage WITH(NOLOCK) Where booking_id = @PRM_booking_id)
//                Set @product_package_fee = ISNULL(@product_package_fee,0)
//                End
//                        Begin
//                /****** product package (medication and service) ******/
//                Set @booking_subtotal_cost_package_prescription = Isnull((Select SUM(quantity * price) From tbl_Booking_ProductPackage WITH(NOLOCK) Where booking_id = @PRM_booking_id And type = 'Medication'),0)
//                Set @booking_subtotal_cost_package_medical_service = Isnull((Select SUM(quantity * price) From tbl_Booking_ProductPackage WITH(NOLOCK) Where booking_id = @PRM_booking_id And type = 'Service'),0)
//                End
//                Begin -- medical service
//                Set @booking_medical_service_fee = ISNULL((Select SUM(price) From tbl_Booking_MedicalService WITH(NOLOCK) where booking_id = @booking_id and package_id is null),0)
//                IF(@booking_is_waive_medical_service = 1)
//                BEGIN
//                SET @booking_waive_medical_service_fee = @booking_medical_service_fee
//                END
//                        ELSE
//                BEGIN
//                SET @booking_waive_medical_service_fee = 0
//                END
//                        End
//                Begin --- Caclulation total cost of prescription ---
//                if(@patient_country_of_residence = 106)
//                Begin
//                Select @booking_total_cost_prescription = SUM(prescription_quantity * third_party_product_price)
//                From tbl_Booking_Prescription WITH(NOLOCK)
//                Where booking_id = @PRM_booking_id and (is_package = 0 or is_package is null)
//                End
//                        Else
//                Begin
//                Select @booking_total_cost_prescription = SUM(prescription_quantity * prescription_price)
//                From tbl_Booking_Prescription WITH(NOLOCK)
//                Where booking_id = @PRM_booking_id and (is_package = 0 or is_package is null)
//                End
//
//
//                Set @booking_total_cost_prescription = [dbo].[fn_FormatRoundNumber](@booking_total_cost_prescription)
//
//                --- calulate waive_medication_fee if medication is waived by doctor---
//                        IF(@booking_is_waive_medication_fee = 1)
//                BEGIN
//                SET @booking_waive_medication_fee = @booking_total_cost_prescription
//                END
//                        ELSE
//                BEGIN
//                SET @booking_waive_medication_fee = 0
//                END
//
//                        --- calulate waive_practice_fee if practice is waived by doctor---
//                        IF(@booking_is_waive_practice_fee = 1)
//                BEGIN
//                SET @booking_waive_practice_fee = @booking_practice_fee
//                END
//                        ELSE
//                BEGIN
//                SET @booking_waive_practice_fee = 0
//                END
//                        End
//                Begin --- Get Corporate Info ---
//                        If(ISNULL(@corporate_member_id, '') != '')
//                Begin
//                If(@not_payment_medication_fee = 1) --> corporate payment medication fee
//                        Begin
//                Set @booking_corporate_payment_pharma = @booking_total_cost_prescription - @booking_waive_medication_fee
//                        End
//                Else
//                        Begin
//                Set @booking_corporate_payment_pharma = 0
//                End
//                        End
//                Else
//                        Begin
//                Set @booking_corporate_payment_pharma = 0
//                End
//                        End
//                Set @booking_corporate_payment_pharma = [dbo].[fn_FormatRoundNumber](@booking_corporate_payment_pharma)
//                        Select @booking_standard_fee = [dbo].[fn_FormatRoundNumber](@booking_total_cost_consulation + @booking_total_cost_prescription +  @surcharge_fee + @booking_practice_fee + @booking_medical_service_fee - @booking_waive_consultation_fee - @booking_waive_medication_fee - @booking_waive_practice_fee - @booking_waive_medical_service_fee + @product_package_fee)
//
//                Begin --- Caclulation total cost pharma for doctor ---
//                        Select @booking_doctor_pharma_total_amount = [dbo].[fn_Get_AmountAfterTaxRateOfBooking](@booking_total_cost_prescription, @PRM_booking_id)
//                End
//
//                Begin --- Update Booking Info ---
//                        Update tbl_Booking
//                Set booking_delivery_fee = 0,
//                        booking_is_paid_prescription = 1,
//                booking_subscription_total_amount = @booking_subscription_total_amount,
//                booking_total_cost_prescription = @booking_total_cost_prescription,
//                booking_total_cost_consulation = @booking_total_cost_consulation,
//                booking_corporate_payment_pharma = @booking_corporate_payment_pharma,
//                booking_standard_fee = @booking_standard_fee,
//                booking_total_amount =  [dbo].[fn_Get_AmountAfterTaxRateOfBooking](@booking_standard_fee, booking_id),
//                booking_corporate_payment_total_amount = (@booking_corporate_payment_consultation + @booking_corporate_payment_pharma),
//                booking_doctor_pharma_total_amount = ISNULL(booking_doctor_pharma_total_amount, 0) + [dbo].[fn_Get_AmountDiscount](@booking_doctor_pharma_total_amount, booking_doctor_pharma_amount, booking_doctor_pharma_type),
//                product_package_fee = @product_package_fee,
//                booking_package_medical_service_fee = @booking_subtotal_cost_package_medical_service,
//                booking_package_prescription_fee = @booking_subtotal_cost_package_prescription,
//                booking_waive_medication_fee = @booking_waive_medication_fee,
//                booking_medical_service_fee = @booking_medical_service_fee,
//                booking_waive_medical_service_fee = @booking_waive_medical_service_fee,
//                update_on = [dbo].[fn_Add_Hours](),
//                        booking_status = CASE WHEN booking_status != -1 AND booking_status < 4 THEN 4 ELSE booking_status END --//fix: #WC-3424 impact online pt force disconnect, consult rate, receipt, SG ID VN KH
//                Where booking_id = @PRM_booking_id
//                        End
//
//                End
//
//                DECLARE @is_push_email_to_aia TINYINT = 0,
//                @aia_specialist_id INT = 0
//
//                SELECT TOP 1 @aia_specialist_id = tbl_Booking_Referral_Letter.specialist_id
//                FROM tbl_Booking_Referral_Letter WITH (NOLOCK)
//                LEFT JOIN tbl_Doctor_Specialist WITH (NOLOCK)
//                        ON tbl_Booking_Referral_Letter.specialist_id = tbl_Doctor_Specialist.specialist_id
//                LEFT JOIN tbl_Specialist_Company WITH (NOLOCK)
//                        ON tbl_Specialist_Company.specialist_company_id = tbl_Doctor_Specialist.specialist_company_id
//                WHERE booking_id = @PRM_booking_id
//                        AND tbl_Specialist_Company.specialist_company_name = 'AIA HealthShield'
//
//                If ISNULL(@aia_specialist_id, 0) > 0
//                Begin
//                set @is_push_email_to_aia = 1
//                End
//
//                Begin -- WC-4740
//                If(@booking_is_waive_medication_fee =1 And (Select COUNT(symptom_name) From @PRM_symptoms Where symptom_name = 'discuss lab results') > 0)
//                Begin
//                Declare @lab_id Uniqueidentifier
//                Select Top 1 @lab_id = Lab_ID
//                From tbl_Lab_Result With(NoLock) Where Mem_ID = Case When @child_id is not null then @child_id else @mem_id end And booking_id Is Null
//                Order By Lab_Screening_Date desc
//                If(@lab_id is not null)
//                Begin
//                Update tbl_Lab_Result Set booking_id = @PRM_booking_id Where Lab_ID = @lab_id
//                        End
//                End
//                        End
//
//                Select  1 As result,
//                @mem_id As mem_id,
//                ISNULL(@booking_queue_no, 0) As booking_queue_no,
//                @booking_consultion_type as booking_consultion_type,
//                @is_push_email_to_aia as is_push_email_to_aia
//                End
//                        End

            }
        }

        /**
         * todo 最后还需要：
         * System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
         *                 {
         *                     member_bl.SendMessageAndResetQueueNo(model.booking_id, "doctor.completebooking", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *                 });
         *                 threadSocket.IsBackground = true;
         *                 threadSocket.Start();
         *                 booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(model), "CompleteBooking").ConfigureAwait(false);
         */

        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp completeOfflineBooking(CompleteOfflineBookingDto dto) {

        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }

        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }

        String memberId = booking.getMemId();
        if (2 == booking.getMemType()) {
            //小孩
            memberId = booking.getPatientMemId();
        }

        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return ResponseResultApp.error("Unauthorised request.");
        }

        if (ObjectUtil.isEmpty(dto.getDiagnosis())) {
            return ResponseResultApp.error("There is no Diagnosis added yet. \n" +
                    "Please check and add a Diagnosis.");
        }
        if (ObjectUtil.isEmpty(booking.getDoctorIsCompleted()) || booking.getDoctorIsCompleted() == 0) {
            return ResponseResultApp.error("Sorry, this booking has been completed.");
        }

        if (dto.getConsult_fee_id().equals("0")) {
            return ResponseResultApp.error("Please select a consultation charges.");
        }
        if (dto.getPrescriptions().isEmpty()) {
            return ResponseResultApp.error("Prescription list must be at least 1 medication");
        }
        TblBookingSummary bookingSummary = tblBookingSummaryMapper.findByBookingId(booking.getId());

        String reasonId = tblFlagMemberMapper.findFlagReasonIdByMemberIdAndByBookingId(memberId, booking.getId());
        if (ObjectUtil.isEmpty(reasonId) && !dto.getFlag_patient_id().equals("0")) {
            return ResponseResultApp.error("The reason id could not be found.");
        }

        dto.setMedical_letter(dto.getMedical_letter() != null ? dto.getMedical_letter() : "");
        if (ObjectUtil.isEmpty(dto.getMedical_certification_begin_date()) || ObjectUtil.isEmpty(dto.getMedical_certification_end_date())) {
            // 创建 Calendar 对象
            Calendar calendar = Calendar.getInstance();
            // 设置日期为1900年1月1日
            calendar.set(Calendar.YEAR, 1900); // 年
            calendar.set(Calendar.MONTH, Calendar.JANUARY); // 月份从0开始计数，0表示1月
            calendar.set(Calendar.DAY_OF_MONTH, 1); // 日
            // 获取 Date 对象
            Date date = calendar.getTime();
            if (ObjectUtil.isEmpty(dto.getMedical_certification_begin_date())) {
                dto.setMedical_certification_begin_date(date);
            }
            if (ObjectUtil.isEmpty(dto.getMedical_certification_end_date())) {
                dto.setMedical_certification_end_date(date);
            }
        }

        if (ObjectUtil.isEmpty(dto.getFlag_patient_id())) {
            return ResponseResultApp.error("The reason id must be larger than 0.");
        }
        if (!"1900/01/01".equals(Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd")) &&
                !"1900/01/01".equals(Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd")) &&
                Kit.calculateDateDifferenceInDays(dto.getMedical_certification_end_date(), dto.getMedical_certification_begin_date()) < 0) {
            return ResponseResultApp.error("The medical certification begin date must be earlier than medical certification end date");
        } else {
            for (CompleteOfflineBookingDto.Prescriptions prescription : dto.getPrescriptions()) {
                if (prescription.getQuantity().compareTo(BigDecimal.ONE) < 0) {
                    return ResponseResultApp.error("Quantity must be larger than 0.");
                }
            }
        }

        if (1 == bookingSummary.getNeedMedicalLetter() && dto.getReferrals().isEmpty()) {
            return ResponseResultApp.error("The medical letter field is required.");
        } else if (1 == bookingSummary.getNeedMedicalCertification() && Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")) {
            return ResponseResultApp.error("The medical certification begin date field is required.");
        } else if (1 == bookingSummary.getNeedMedicalCertification() && Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
            return ResponseResultApp.error("The medical certification end date field is required.");
        }
        if (3 != booking.getStatus() && 4 != booking.getStatus() && 5 != booking.getStatus() && 99 != booking.getStatus() && 6 != booking.getStatus()) {
            if (99 == booking.getStatus()) {
                return ResponseResultApp.error("Sorry, this booking has been completed.");
            } else {
                return ResponseResultApp.error("Booking invalid.");
            }
        }

        //删除患者过敏
        tblMemberAllergiesMapper.updateDelFlagTrueByMemberId(memberId);
        //检查数据存在则开始保存
        if (ObjectUtil.isNotEmpty(dto.getAllergies())) {
            for (CompleteOfflineBookingDto.Allergies allergy : dto.getAllergies()) {
                if (ObjectUtil.isNotEmpty(allergy.getName())) {
                    TblBookingAllergies bookingAllergies = new TblBookingAllergies();
                    bookingAllergies.setBookingId(booking.getId());
                    bookingAllergies.setAllergiesName(allergy.getName());
                    tblBookingAllergiesMapper.insertSelective(bookingAllergies);

                    TblMemberAllergies memberAllergies = new TblMemberAllergies();
                    memberAllergies.setMemId(memberId);
                    memberAllergies.setAllergiesName(allergy.getName());
                    tblMemberAllergiesMapper.insertSelective(memberAllergies);
                }
            }
        }
        //Insert tbl_Booking_MedicationReaction
        tblBookingAllergiesMapper.updateDelFlagTrueByBookingId(booking.getId());
        tblMemberAllergiesMapper.updateDelFlagTrueByMemberId(memberId);
        if (ObjectUtil.isNotEmpty(dto.getMedication_reactions())) {
            for (CompleteOfflineBookingDto.MedicationReactions reaction : dto.getMedication_reactions()) {
                TblMedicationReaction byId = tblMedicationReactionMapper.findById(reaction.getId());
                if (ObjectUtil.isEmpty(byId)) {
                    //不存在数据库 - 则需要添加
                    TblMedicationReaction medicationReaction = new TblMedicationReaction();
                    medicationReaction.setId(reaction.getId());
                    medicationReaction.setName(reaction.getName());
                    medicationReaction.setStatus(1);
                    medicationReaction.setDelFlag(0);
                    medicationReaction.setGroupId(doctor.getGroupId());
                    medicationReaction.setCreatedBy(doctor.getId());
                    medicationReaction.setCreatedOnUtc(new Date().getTime());
                    tblMedicationReactionMapper.insertSelective(medicationReaction);
                }
                //对方：tbl_Booking_MedicationReaction - 我们整个至tbl_booking_allergies
                TblBookingAllergies bookingAllergies = new TblBookingAllergies();
                bookingAllergies.setBookingId(booking.getId());
                bookingAllergies.setMedicationReactionId(reaction.getId());
                bookingAllergies.setDelFlag(0);
                bookingAllergies.setCreatedBy(doctor.getId());
                bookingAllergies.setCreatedOnUtc(new Date().getTime());
                tblBookingAllergiesMapper.insertSelective(bookingAllergies);

                //Insert tbl_Member_MedicationReactions - 我们整合至：tbl_member_allergies表
                TblMemberAllergies memberAllergies = new TblMemberAllergies();
                memberAllergies.setMemId(memberId);
                memberAllergies.setMedicationReactionId(reaction.getId());
                memberAllergies.setDelFlag(0);
                memberAllergies.setCreatedBy(doctor.getId());
                memberAllergies.setCreatedOnUtc(new Date().getTime());
                tblMemberAllergiesMapper.insertSelective(memberAllergies);
            }
        }

        //Insert tbl_Booking_Symptoms
        //[dbo].[API_UpdateBookingSymptom] @PRM_doctor_id, 1, @PRM_booking_id, @PRM_symptoms
        tblBookingSymptomMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getSymptoms())) {
            Integer order = tblSymptomMapper.findOrderByGroupId(doctor.getGroupId());
            for (CompleteOfflineBookingDto.Symptoms dtoSymptom : dto.getSymptoms()) {
                TblSymptom byNameAndByGroupId = tblSymptomMapper.findByNameAndByGroupId(dtoSymptom.getName(), doctor.getGroupId());
                if (ObjectUtil.isEmpty(byNameAndByGroupId)) {
                    TblSymptom symptom = new TblSymptom();
                    order++;
                    symptom.setDispOrder(order);
                    symptom.setName(dtoSymptom.getName());
                    symptom.setGroupId(doctor.getGroupId());
                    tblSymptomMapper.insertSelective(symptom);
                }
                TblSymptom db = tblSymptomMapper.findByNameAndByGroupId(dtoSymptom.getName(), doctor.getGroupId());
                if (ObjectUtil.isNotEmpty(db)) {
                    TblBookingSymptom bookingSymptom = new TblBookingSymptom();
                    bookingSymptom.setBookingId(booking.getId());
                    bookingSymptom.setSymptomId(db.getId());
                    tblBookingSymptomMapper.insertSelective(bookingSymptom);
                }
            }
        }

        //Update tbl_Booking_Consultation
        TblBookingConsultation bookingConsultation = tblBookingConsultationMapper.findByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(bookingConsultation)) {
            // 计算时间差（以毫秒为单位）
            long timeDifference = new Date().getTime() - bookingConsultation.getBeginTimeUtc().getTime();
            // 计算时间差的小时、分钟和秒
            long hours = timeDifference / (60 * 60 * 1000);
            long minutes = (timeDifference % (60 * 60 * 1000)) / (60 * 1000);
            long seconds = ((timeDifference % (60 * 60 * 1000)) % (60 * 1000)) / 1000;
            // 格式化输出时间差
            String timeDifferenceStr = String.format("%02d:%02d:%02d", hours, minutes, seconds);
            bookingConsultation.setDuration(timeDifferenceStr);
            bookingConsultation.setStatus(1);
            tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
        }
        if (dto.getFlag_patient_id().equals("0")) {
            dto.setFlag_patient_id(null);
        }
        TblBookingFee bookingFee = tblBookingFeeMapper.findByBookingId(booking.getId());
        //Update tbl_Booking Of Patient Current
        bookingSummary.setPresentingComplaint(dto.getPresenting_complaint() != null ? dto.getPresenting_complaint() : null);
        bookingSummary.setManagement(dto.getMamagement() != null ? dto.getMamagement() : null);
        bookingSummary.setInstructionsToPatient(dto.getInstructions_to_patient() != null ? dto.getInstructions_to_patient() : null);
        //    booking_reason = @PRM_reason,  字段不存在
        //    booking_reason_id = @PRM_reason_id,  字段不存在
        bookingSummary.setPatientWeight(dto.getPatient_weight() != null ? dto.getPatient_weight() : null);
        bookingSummary.setPatientHeight(dto.getPatient_height() != null ? dto.getPatient_height() : null);
        bookingSummary.setPatientTemp(dto.getPatient_temp() != null ? dto.getPatient_temp() : null);
        bookingSummary.setPatientBp1(dto.getPatient_bp1() != null ? dto.getPatient_bp1() : null);
        bookingSummary.setPatientBp2(dto.getPatient_bp2() != null ? dto.getPatient_bp2() : null);
        bookingSummary.setPatientHr(dto.getPatient_hr() != null ? dto.getPatient_hr() : null);
        bookingSummary.setBasicCdmpInformation(dto.getBasic_cdmp_information() != null ? dto.getBasic_cdmp_information() : null);
        booking.setIsCdmpChronic(dto.getIs_cdmp_chronic() != null ? dto.getIs_cdmp_chronic() ? 1 : 0 : null);
        booking.setFollowUpConsultationNo(dto.getFollow_up_code() != null ? dto.getFollow_up_code() : null);
        bookingFee.setConsultFeeId(dto.getConsult_fee_id() != null ? dto.getConsult_fee_id() : null);
        bookingFee.setConsultFeeText(dto.getConsult_fee_text() != null ? dto.getConsult_fee_text() : null);
        if (ObjectUtil.isNotEmpty(bookingSummary.getNeedMedicalCertification()) && 0 != bookingSummary.getNeedMedicalCertification()) {
            if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")
                    && !Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationCode("MC" + booking.getConsultationNo());
                bookingSummary.setAdditionalNote(dto.getMedical_certification_additional_note() != null ? dto.getMedical_certification_additional_note() : null);
            }
            if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationBeginDate(dto.getMedical_certification_begin_date());
            }
            if (!Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationEndDate(dto.getMedical_certification_end_date());
            }
        }
        tblBookingQueueMapper.updateQueueNoSetNullByBookingId(booking.getId());
        booking.setDoctorIsCompleted(1);
        bookingSummary.setSummary(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
        bookingSummary.setDiagnosisAllowPatientToClaim(dto.getDiagnosis_allow_patient_to_claim() != null ? dto.getDiagnosis_allow_patient_to_claim() ? 1 : 0 : null);
        tblBookingMapper.updateByPrimaryKeySelective(booking);
        tblBookingSummaryMapper.updateByPrimaryKeySelective(bookingSummary);
        tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);

        //Update tbl_member patient vitals and summary
        TblMember member = tblMemberMapper.findById(memberId);
        TblMemberSummary memberSummary = tblMemberSummaryMapper.findOneByBookingId(booking.getId());
        if (ObjectUtil.isEmpty(memberSummary)) {
            memberSummary = new TblMemberSummary();
            memberSummary.setMemberId(memberId);
            memberSummary.setBody(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
            memberSummary.setDoctorId(doctor.getId());
            tblMemberSummaryMapper.insertSelective(memberSummary);
        } else {
            memberSummary.setMemberId(memberId);
            memberSummary.setBody(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
            memberSummary.setDoctorId(doctor.getId());
            tblMemberSummaryMapper.updateByPrimaryKeySelective(memberSummary);
        }

        //Update patient’s measurement
        if (2 == booking.getMemType()) {
            //todo exec [dbo].[spCheckConsultAndLabResult] @PRM_Mem_Id=@child_id,@PRM_Booking_Id=@PRM_booking_id,@PRM_Doctor_Id=@PRM_doctor_id,@PRM_Weight=@PRM_patient_weight,@PRM_Height=@PRM_patient_height
            spCheckConsultAndLabResult(member, booking, doctor);
        } else {
            //todo [dbo].[spCheckConsultAndLabResult] @PRM_Mem_Id=@mem_id,@PRM_Booking_Id=@PRM_booking_id,@PRM_Doctor_Id=@PRM_doctor_id,@PRM_Weight=@PRM_patient_weight,@PRM_Height=@PRM_patient_height
        }

        //Update Diagnosis
        tblBookingDiagnosisMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getDiagnosis())) {
            for (CompleteOfflineBookingDto.Diagnosis diagnosis : dto.getDiagnosis()) {
                if (ObjectUtil.isNotEmpty(diagnosis.getName())) {
                    //不存在才添加
                    if (ObjectUtil.isEmpty(tblDiagnosisMapper.findByGroupIdAndByNameOrCode(doctor.getGroupId(), diagnosis.getName()))) {
                        TblDiagnosis tblDiagnosis = new TblDiagnosis();
                        tblDiagnosis.setGroupId(doctor.getGroupId());
                        tblDiagnosis.setName(diagnosis.getName());
                        tblDiagnosis.setDispOrder(diagnosis.getOrdering() != null ? diagnosis.getOrdering() : null);
                        tblDiagnosis.setCountryOfResidence(booking.getDoctorCountryOfResidence());
                        tblDiagnosisMapper.insertSelective(tblDiagnosis);
                    }
                    TblDiagnosis findDiagnosis = tblDiagnosisMapper.findByNameAndByGroupIdAndByDoctorCountryId(diagnosis.getName(), doctor.getGroupId(), booking.getDoctorCountryOfResidence());
                    if (ObjectUtil.isNotEmpty(findDiagnosis)) {
                        TblBookingDiagnosis bookingDiagnosis = new TblBookingDiagnosis();
                        bookingDiagnosis.setBookingId(booking.getId());
                        bookingDiagnosis.setDiagnosisId(findDiagnosis.getId());
                        //这个表对方还设置了is_in_exclusion_list 但是我们TblBookingDiagnosis和TblDiagnosis都没有所以忽略
                        tblBookingDiagnosisMapper.insertSelective(bookingDiagnosis);
                    }
                }
            }
        }

        //Update Referral Letter
        tblBookingReferralLetterMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getReferrals())) {
            for (CompleteOfflineBookingDto.Referrals referral : dto.getReferrals()) {
                TblBookingReferralLetter bookingReferralLetter = new TblBookingReferralLetter();
                bookingReferralLetter.setBookingId(booking.getId());
                bookingReferralLetter.setSpecialistId(referral.getSpecialist_id() != null ? referral.getSpecialist_id() : null);
                bookingReferralLetter.setSpecialisationId(referral.getMDST_ID() != null ? referral.getMDST_ID() : null);
                bookingReferralLetter.setReferralLetterText(referral.getReferral_letter_text() != null ? referral.getReferral_letter_text() : null);
                bookingReferralLetter.setReferralLetterCode(referral.getReferral_letter_code() != null ? "ML" + booking.getConsultationNo() + referral.getReferral_letter_code() : null);
                tblBookingReferralLetterMapper.insertSelective(bookingReferralLetter);
            }
        }

        //delete queue
        tblBookingQueueMapper.deleteByBookingId(booking.getId());

        //ADD LOG QUEUE : booking_id - action - sub_action - driver - date_time - doctor_id
        //todo EXEC [dbo].[API_Insert_Booking_Log] @PRM_booking_id, 100, 6, '', null, null
        if (0 == CoreAppConstants.IS_BOOKING_QUEUE_ENABLED) {
            /**
             *         IF @PRM_Is_Implement_PTMQ = 0 --WC-3201
             *         BEGIN
             *             -- Reset Queue for patient
             *             IF dbo.fn_check_SP_Doctor(@PRM_doctor_id) = 0
             *             BEGIN
             *                 exec dbo.Reset_Num_Patient_InFrontOf
             *             END
             *             ELSE
             *             BEGIN
             *                 exec dbo.Reset_Num_Patient_InFrontOf_SP @PRM_doctor_id
             *             END
             *         END
             */
            if (2 == doctor.getPanelType()) {
                //GP

            } else {
                //SP
            }
        }

        if (5 == booking.getConsultationMode()) {
            TblBookingMedicalService bookingMedicalService = new TblBookingMedicalService();
            Integer count = tblBookingMedicalServiceMapper.findCountByBookingIdAndInServiceId(booking.getId());
            /**
             * todo 这俩if和else的sql下方都缺少：
             * 	Select TOP(1) @driver_id = driver_id
             * 	From tbl_Booking WITH(NOLOCK)
             * 	Where booking_id = @PRM_booking_id
             *
             * 	Exec [dbo].[API_Insert_Booking_Log] @PRM_booking_id, @PRM_booking_status, @PRM_booking_sub_status, @driver_id
             */
            if (count >= 1) {
                //Exec [dbo].[sp_UpdateBookingStatus] @PRM_booking_id, 4, -3
                booking.setStatus(4);
                booking.setSubStatus(-3);
                tblBookingMapper.updateByPrimaryKeySelective(booking);
            } else {
                //Exec [dbo].[sp_UpdateBookingStatus] @PRM_booking_id, 99, 1
                booking.setStatus(99);
                booking.setSubStatus(1);
                tblBookingMapper.updateByPrimaryKeySelective(booking);
            }
        }

        //lock patient info
        if (ObjectUtil.isEmpty(tblBookingMemberInfoMapper.findByBookingId(booking.getId()))) {
            TblBookingMemberInfo bookingMemberInfo = new TblBookingMemberInfo();
            TblMember mem = null;
            TblMember child = null;
            if (1 == booking.getConsultationMode()) {
                mem = member;
            } else {
                child = member;
                mem = tblMemberMapper.findById(booking.getMemId());
            }
            bookingMemberInfo.setBookingId(booking.getId());
            if (ObjectUtil.isNotEmpty(mem)) {
                bookingMemberInfo.setMemberId(member.getId());
                bookingMemberInfo.setMemberDob(member.getDob());
                bookingMemberInfo.setMemberEmail(member.getEmail());
                bookingMemberInfo.setMemberFirstName(member.getFirstName());
                bookingMemberInfo.setMemberLastName(member.getLastName() != null ? member.getLastName() : null);
                bookingMemberInfo.setMemberGender(member.getGender());
                bookingMemberInfo.setMemberNricFin(member.getNricFin());
                bookingMemberInfo.setMemberPhoneNumber(member.getPhone());
            }
            if (ObjectUtil.isNotEmpty(child)) {
                bookingMemberInfo.setChildId(child.getId());
                bookingMemberInfo.setChildDob(child.getDob());
                bookingMemberInfo.setChildEmail(child.getEmail());
                bookingMemberInfo.setChildFirstName(child.getFirstName());
                bookingMemberInfo.setChildLastName(child.getLastName() != null ? member.getLastName() : null);
                bookingMemberInfo.setChildGender(child.getGender());
                bookingMemberInfo.setChildNricFin(child.getNricFin());
                bookingMemberInfo.setChildPhoneNumber(child.getPhone());
            }
            tblBookingMemberInfoMapper.insertSelective(bookingMemberInfo);
        }
        //Caclulation total cost of consulation
        //todo 剩余下方的计算以及最终修改了
//        Begin --- Caclulation total cost of consulation ---
//                Select @booking_total_cost_consulation = SUM(consulation_amount) From tbl_Booking_Consultation Where booking_id = @PRM_booking_id
//                Set @booking_total_cost_consulation = [dbo].[fn_FormatRoundNumber](@booking_total_cost_consulation)
//                End
//        Begin --- Caclulation total product package fee ---
//                Select @product_package_fee = (Select SUM(price * quantity) From tbl_Booking_ProductPackage Where booking_id = @PRM_booking_id)
//        Set @product_package_fee = ISNULL(@product_package_fee,0)
//        End
//                Begin
//        /****** product package (medication and service) ******/
//        Set @booking_subtotal_cost_package_prescription = Isnull((Select SUM(quantity * price) From tbl_Booking_ProductPackage Where booking_id = @PRM_booking_id And type = 'Medication'),0)
//        Set @booking_subtotal_cost_package_medical_service = Isnull((Select SUM(quantity * price) From tbl_Booking_ProductPackage Where booking_id = @PRM_booking_id And type = 'Service'),0)
//        End
//        Begin -- medical service
//        Set @booking_medical_service_fee = ISNULL((Select SUM(price) From tbl_Booking_MedicalService where booking_id = @booking_id and package_id is null),0)
//        IF(@booking_is_waive_medical_service = 1)
//        BEGIN
//        SET @booking_waive_medical_service_fee = @booking_medical_service_fee
//        END
//                ELSE
//        BEGIN
//        SET @booking_waive_medical_service_fee = 0
//        END
//                End
//        Begin --- Caclulation total cost of prescription ---
//        if(@patient_country_of_residence = 106)
//        Begin
//        Select @booking_total_cost_prescription = SUM(prescription_quantity * third_party_product_price)
//        From tbl_Booking_Prescription
//        Where booking_id = @PRM_booking_id and (is_package = 0 or is_package is null)
//        End
//                Else
//        Begin
//        Select @booking_total_cost_prescription = SUM(prescription_quantity * prescription_price)
//        From tbl_Booking_Prescription
//        Where booking_id = @PRM_booking_id and (is_package = 0 or is_package is null)
//        End
//
//
//        Set @booking_total_cost_prescription = [dbo].[fn_FormatRoundNumber](@booking_total_cost_prescription)
//
//        --- calulate waive_medication_fee if medication is waived by doctor---
//                IF(@booking_is_waive_medication_fee = 1)
//        BEGIN
//        SET @booking_waive_medication_fee = @booking_total_cost_prescription
//        END
//                ELSE
//        BEGIN
//        SET @booking_waive_medication_fee = 0
//        END
//
//                --- calulate waive_practice_fee if practice is waived by doctor---
//                IF(@booking_is_waive_practice_fee = 1)
//        BEGIN
//        SET @booking_waive_practice_fee = @booking_practice_fee
//        END
//                ELSE
//        BEGIN
//        SET @booking_waive_practice_fee = 0
//        END
//                End
//        Begin --- Get Corporate Info ---
//                If(ISNULL(@corporate_member_id, '') != '')
//        Begin
//        If(@not_payment_medication_fee = 1) --> corporate payment medication fee
//                Begin
//        Set @booking_corporate_payment_pharma = @booking_total_cost_prescription - @booking_waive_medication_fee
//                End
//        Else
//                Begin
//        Set @booking_corporate_payment_pharma = 0
//        End
//                End
//        Else
//                Begin
//        Set @booking_corporate_payment_pharma = 0
//        End
//                End
//        Set @booking_corporate_payment_pharma = [dbo].[fn_FormatRoundNumber](@booking_corporate_payment_pharma)
//                Select @booking_standard_fee = [dbo].[fn_FormatRoundNumber](@booking_total_cost_consulation + @booking_total_cost_prescription +  @surcharge_fee + @booking_practice_fee + @booking_medical_service_fee - @booking_waive_consultation_fee - @booking_waive_medication_fee - @booking_waive_practice_fee - @booking_waive_medical_service_fee + @product_package_fee)
//        Begin --- Caclulation total cost pharma for doctor ---
//                Select @booking_doctor_pharma_total_amount = [dbo].[fn_Get_AmountAfterTaxRateOfBooking](@booking_total_cost_prescription, booking_id)
//        From tbl_Booking
//        Where booking_id = @PRM_booking_id
//                End
//        Begin --- Update Booking Info ---
//                Update tbl_Booking
//        Set booking_delivery_fee = 0,
//                booking_is_paid_prescription = 1,
//        booking_subscription_total_amount = @booking_subscription_total_amount,
//        booking_total_cost_prescription = @booking_total_cost_prescription,
//        booking_total_cost_consulation = @booking_total_cost_consulation,
//        booking_corporate_payment_pharma = @booking_corporate_payment_pharma,
//        booking_standard_fee = @booking_standard_fee,
//        booking_total_amount =  [dbo].[fn_Get_AmountAfterTaxRateOfBooking](@booking_standard_fee, booking_id),
//        booking_corporate_payment_total_amount = (@booking_corporate_payment_consultation + @booking_corporate_payment_pharma),
//        booking_doctor_pharma_total_amount = ISNULL(booking_doctor_pharma_total_amount, 0) + [dbo].[fn_Get_AmountDiscount](@booking_doctor_pharma_total_amount, booking_doctor_pharma_amount, booking_doctor_pharma_type),
//        product_package_fee = @product_package_fee,
//        booking_package_medical_service_fee = @booking_subtotal_cost_package_medical_service,
//        booking_package_prescription_fee = @booking_subtotal_cost_package_prescription,
//        booking_waive_medication_fee = @booking_waive_medication_fee,
//        booking_medical_service_fee = @booking_medical_service_fee,
//        booking_waive_medical_service_fee = @booking_waive_medical_service_fee,
//        update_on = [dbo].[fn_Add_Hours]()
//        Where booking_id = @PRM_booking_id
//                End
//
//        End
//        declare @is_push_email_to_aia tinyInt = 0, @aia_specialist_id int = 0
//        select top 1 @aia_specialist_id = tbl_Booking_Referral_Letter.specialist_id
//        from tbl_Booking_Referral_Letter
//        left join tbl_Doctor_Specialist on tbl_Booking_Referral_Letter.specialist_id = tbl_Doctor_Specialist.specialist_id
//        left join tbl_Specialist_Company on tbl_Specialist_Company.specialist_company_id = tbl_Doctor_Specialist.specialist_company_id
//        where booking_id = @PRM_booking_id and tbl_Specialist_Company.specialist_company_name = 'AIA HealthShield'
//        If ISNULL(@aia_specialist_id, 0) > 0
//        Begin
//        set @is_push_email_to_aia = 1
//        End
//                --select top 1 @is_push_email_to_aia = Case When tbl_Specialist_Company.specialist_company_name = 'AIA HealthShield' then 1 else 0 end
//                --from tbl_Booking
//        --left join tbl_Doctor_Specialist on tbl_Booking.specialist_id = tbl_Doctor_Specialist.specialist_id
//                --left join tbl_Specialist_Company on tbl_Specialist_Company.specialist_company_id = tbl_Doctor_Specialist.specialist_company_id
//                --where @PRM_booking_id = booking_id
//
//        Select  1 As result,
//        @mem_id As mem_id,
//        ISNULL(@booking_queue_no, 0) As booking_queue_no,
//        @booking_consultion_type as booking_consultion_type,
//        @is_push_email_to_aia as is_push_email_to_aia
//        End
//                End
        /**
         * todo：以及剩余：
         *  System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
         *                 {
         *                     member_bl.SendMessageAndResetQueueNo(model.booking_id, "doctor.completebooking", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *                 });
         *                 threadSocket.IsBackground = true;
         *                 threadSocket.Start();
         *                 booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(model), "CompleteOfflineBooking").ConfigureAwait(false);
         *                 return response;
         */
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp completeTBTCBooking(CompleteOfflineBookingDto dto) {
        if (dto.getConsult_fee_id().equals("0")) {
            return ResponseResultApp.error("Please select a consultation charges.");
        }

        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }

        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }

        String memberId = booking.getMemId();
        if (2 == booking.getMemType()) {
            //小孩
            memberId = booking.getPatientMemId();
        }

        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return ResponseResultApp.error("Unauthorised request.");
        }

        if (ObjectUtil.isEmpty(booking.getDoctorIsCompleted()) || booking.getDoctorIsCompleted() == 0) {
            return ResponseResultApp.error("Sorry, this booking has been completed.");
        }

        String reasonId = tblFlagMemberMapper.findFlagReasonIdByMemberIdAndByBookingId(memberId, booking.getId());
        if (ObjectUtil.isEmpty(reasonId) && !dto.getFlag_patient_id().equals("0")) {
            return ResponseResultApp.error("The reason id could not be found.");
        }

        TblBookingSummary bookingSummary = tblBookingSummaryMapper.findByBookingId(booking.getId());
        if (1 == bookingSummary.getNeedMedicalLetter() && dto.getReferrals().isEmpty()) {
            return ResponseResultApp.error("The medical letter field is required.");
        } else if (1 == bookingSummary.getNeedMedicalCertification() && Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")) {
            return ResponseResultApp.error("The medical certification begin date field is required.");
        } else if (1 == bookingSummary.getNeedMedicalCertification() && Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
            return ResponseResultApp.error("The medical certification end date field is required.");
        }

        dto.setMedical_letter(dto.getMedical_letter() != null ? dto.getMedical_letter() : "");
        if (ObjectUtil.isEmpty(dto.getMedical_certification_begin_date()) || ObjectUtil.isEmpty(dto.getMedical_certification_end_date())) {
            // 创建 Calendar 对象
            Calendar calendar = Calendar.getInstance();
            // 设置日期为1900年1月1日
            calendar.set(Calendar.YEAR, 1900); // 年
            calendar.set(Calendar.MONTH, Calendar.JANUARY); // 月份从0开始计数，0表示1月
            calendar.set(Calendar.DAY_OF_MONTH, 1); // 日
            // 获取 Date 对象
            Date date = calendar.getTime();
            if (ObjectUtil.isEmpty(dto.getMedical_certification_begin_date())) {
                dto.setMedical_certification_begin_date(date);
            }
            if (ObjectUtil.isEmpty(dto.getMedical_certification_end_date())) {
                dto.setMedical_certification_end_date(date);
            }
        }

        if (ObjectUtil.isEmpty(dto.getFlag_patient_id())) {
            return ResponseResultApp.error("The reason id must be larger than 0.");
        }
        if (!"1900/01/01".equals(Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd")) &&
                !"1900/01/01".equals(Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd")) &&
                Kit.calculateDateDifferenceInDays(dto.getMedical_certification_end_date(), dto.getMedical_certification_begin_date()) < 0) {
            return ResponseResultApp.error("The medical certification begin date must be earlier than medical certification end date");
        } else {
            for (CompleteOfflineBookingDto.Prescriptions prescription : dto.getPrescriptions()) {
                if (prescription.getQuantity().compareTo(BigDecimal.ONE) < 0) {
                    return ResponseResultApp.error("Quantity must be larger than 0.");
                }
            }
        }

        //Update tbl_Booking_Consultation
        TblBookingConsultation bookingConsultation = tblBookingConsultationMapper.findByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(bookingConsultation)) {
            // 计算时间差（以毫秒为单位）
            long timeDifference = new Date().getTime() - bookingConsultation.getBeginTimeUtc().getTime();
            // 计算时间差的小时、分钟和秒
            long hours = timeDifference / (60 * 60 * 1000);
            long minutes = (timeDifference % (60 * 60 * 1000)) / (60 * 1000);
            long seconds = ((timeDifference % (60 * 60 * 1000)) % (60 * 1000)) / 1000;
            // 格式化输出时间差
            String timeDifferenceStr = String.format("%02d:%02d:%02d", hours, minutes, seconds);
            bookingConsultation.setDuration(timeDifferenceStr);
            bookingConsultation.setStatus(1);
            tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
        }
        if (dto.getFlag_patient_id().equals("0")) {
            dto.setFlag_patient_id(null);
        }
        Integer needMedicalLetter = 0;
        if (ObjectUtil.isNotEmpty(dto.getReferrals())) {
            needMedicalLetter = 1;
        }

        //Update tbl_Booking Of Patient Current
        bookingSummary.setInstructionsToPatient(dto.getInstructions_to_patient() != null ? dto.getInstructions_to_patient() : null);
        /**
         * todo 缺少字段
         * booking_reason = @PRM_reason,
         * booking_reason_id = @PRM_reason_id,
         */
        if (ObjectUtil.isNotEmpty(bookingSummary.getNeedMedicalCertification()) && 0 != bookingSummary.getNeedMedicalCertification()) {
            if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")
                    && !Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationCode("MC" + booking.getConsultationNo());
                bookingSummary.setAdditionalNote(dto.getMedical_certification_additional_note() != null ? dto.getMedical_certification_additional_note() : null);
            }
            if (!Kit.formatDate(dto.getMedical_certification_begin_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationBeginDate(dto.getMedical_certification_begin_date());
            }
            if (!Kit.formatDate(dto.getMedical_certification_end_date(), "yyyy/MM/dd").equals("1900/01/01")) {
                bookingSummary.setMedicalCertificationEndDate(dto.getMedical_certification_end_date());
            }
        }
        tblBookingQueueMapper.updateQueueNoSetNullByBookingId(booking.getId());
        booking.setDoctorIsCompleted(1);
        bookingSummary.setSummary(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
        if (1 != booking.getStatus() && booking.getStatus() < 4) {
            booking.setStatus(4);
        }
        bookingSummary.setNeedMedicalLetter(needMedicalLetter);
        tblBookingMapper.updateByPrimaryKeySelective(booking);
        tblBookingSummaryMapper.updateByPrimaryKeySelective(bookingSummary);

        //Update tbl_member patient vitals and summary
        TblMember member = tblMemberMapper.findById(memberId);
        TblMemberSummary memberSummary = tblMemberSummaryMapper.findOneByBookingId(booking.getId());
        if (ObjectUtil.isEmpty(memberSummary)) {
            memberSummary = new TblMemberSummary();
            memberSummary.setMemberId(memberId);
            memberSummary.setBody(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
            memberSummary.setDoctorId(doctor.getId());
            tblMemberSummaryMapper.insertSelective(memberSummary);
        } else {
            memberSummary.setMemberId(memberId);
            memberSummary.setBody(dto.getPatient_summary() != null ? dto.getPatient_summary() : null);
            memberSummary.setDoctorId(doctor.getId());
            tblMemberSummaryMapper.updateByPrimaryKeySelective(memberSummary);
        }

        //Update Referral Letter
        tblBookingReferralLetterMapper.updateDelFlagTrueByBookingId(booking.getId());
        if (ObjectUtil.isNotEmpty(dto.getReferrals())) {
            for (CompleteOfflineBookingDto.Referrals referral : dto.getReferrals()) {
                TblBookingReferralLetter bookingReferralLetter = new TblBookingReferralLetter();
                bookingReferralLetter.setBookingId(booking.getId());
                bookingReferralLetter.setSpecialistId(referral.getSpecialist_id() != null ? referral.getSpecialist_id() : null);
                bookingReferralLetter.setSpecialisationId(referral.getMDST_ID() != null ? referral.getMDST_ID() : null);
                bookingReferralLetter.setReferralLetterText(referral.getReferral_letter_text() != null ? referral.getReferral_letter_text() : null);
                bookingReferralLetter.setReferralLetterCode(referral.getReferral_letter_code() != null ? "ML" + booking.getConsultationNo() + referral.getReferral_letter_code() : null);
                tblBookingReferralLetterMapper.insertSelective(bookingReferralLetter);
            }
        }

        //Update follow up action
        TblBookingTextBased bookingTextBased = tblBookingTextBasedMapper.findByBookingId(booking.getId());
        bookingTextBased.setFollowUpAction(dto.getIs_follow_up_video_consult() != null ? dto.getIs_follow_up_video_consult() ? 1 : 0 : null);
        tblBookingTextBasedMapper.updateByPrimaryKeySelective(bookingTextBased);

        //ADD LOG QUEUE : booking_id - action - sub_action - driver - date_time - doctor_id
        //todo EXEC [dbo].[API_Insert_Booking_Log] @PRM_booking_id, 100, 6, '', null, null
        TblBookingReferralLetter bookingReferralLetter = new TblBookingReferralLetter();
        String specialistId = tblBookingReferralLetterMapper.findSpecialistIdByBookingIdAndByName(booking.getId(), "AIA HealthShield");
        Integer isPushEmailToAia = 0;
        if (ObjectUtil.isNotEmpty(specialistId)) {
            isPushEmailToAia = 1;
        }

        /**
         * todo
         * await new SQSHelper().ResetNumberOfPatientInFrontOfAsync(new ResetNumberOfPatientInFrontOfMessage
         *                                 {
         *                                     DoctorId = account_id
         *                                 });
         */
        if (1 == isPushEmailToAia) {
            //todo BackgroundJob.Enqueue(() => GetDeviceToPush(model.booking_id, account_id, (int)AccountType.Member, Constants.API_Notification_Type.PATIENT.BOOKING_COMPLETED_AND_PUSH_AIA_NOTIFICATION, "", booking_consultion_type, "", ""));
        }
        /**
         * todo 未做的
         * System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
         *                 {
         *                     member_bl.SendMessageAndResetQueueNo(model.booking_id, "doctor.completetbtcbooking", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *                 });
         *                 threadSocket.IsBackground = true;
         *                 threadSocket.Start();
         *                 booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(model), "CompleteTBTCBooking").ConfigureAwait(false);
         */

        return ResponseResultApp.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp endCall(EndCallDto dto) {
        if (dto.getWaive_consultation_fee() && dto.getReason().isEmpty() && 0 == dto.getWaive_consultation_fee_type()) {
            return ResponseResultApp.error("The reason field is required.");
        }
        if (!dto.getWaive_consultation_fee()) {
            dto.setReason("");
            dto.setWaive_consultation_fee_type(null);
        }

        return null;
    }

    /**
     * 函数：API_DoctorEndCallOfflineBooking
     *
     * @param dto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp endCallOfflineBooking(EndCallDto dto) throws ParseException {

        if (dto.getWaive_consultation_fee() && dto.getReason().isEmpty() && 0 == dto.getWaive_consultation_fee_type()) {
            return ResponseResultApp.error("The reason field is required.");
        }

        if (!dto.getWaive_consultation_fee()) {
            dto.setReason("");
            dto.setWaive_consultation_fee_type(null);
        }

        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }

        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("Booking invalid");
        }

        String memberId = booking.getMemId();
        if (2 == booking.getMemType()) {
            //小孩
            memberId = booking.getPatientMemId();
        }

        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return ResponseResultApp.error("Unauthorised request.");
        }
        //wc-1394 check drug's available quantity before endcall for offline consult
        List<CheckNotEnoughInInventory> checkNotEnoughInInventories = tblProductMapper.findCheckNotEnoughInInventory(booking.getId(), "245");
        Integer checkNotEnoughInInventoriesSize = 0;
        if (ObjectUtil.isNotEmpty(checkNotEnoughInInventories)) {
            checkNotEnoughInInventoriesSize = checkNotEnoughInInventories.size();
        }
        //如果有存在库存不足的药品则返回错误信息
        if (checkNotEnoughInInventoriesSize > 0) {
            StringBuilder builder = new StringBuilder();
            builder.append("<ul>");
            checkNotEnoughInInventories.forEach(item -> {
                builder.append("<li>")
                        .append("The quantity of ")
                        .append(item.getName())
                        .append(" in the inventory is not enough for your prescription.&lt;br&gt;&lt;strong&gt;Quantity left = ")
                        .append(item.getQuantityLeft())
                        .append(" ")
                        .append(item.getQuantityType())
                        .append("&lt;/strong&gt;")
                        .append("</li>");
            });
            builder.append("</ul>");
            return ResponseResultApp.error(builder.toString());
        }
        //EAP Check
        String employeeAssistanceProgramBenefitId = null;
        String medicalServiceExclusionListId = null;
        String medicalServiceInclusionListId = null;
        if (1 == booking.getPlanType() || 2 == booking.getPlanType() && ObjectUtil.isNotEmpty(booking.getBenefitId())) {
            EndCallOfflineBookingCheckEap checkEap = tblBookingEapMapper.findEndCallOfflineBookingCheckEapByBookingId(booking.getId());
            employeeAssistanceProgramBenefitId = checkEap.getEmployeeAssistanceProgramBenefitId() != null ? checkEap.getEmployeeAssistanceProgramBenefitId() : null;
            medicalServiceExclusionListId = checkEap.getMedicalServiceExclusionListId() != null ? checkEap.getMedicalServiceExclusionListId() : null;
        }

        //AIA Corporate
        if (1 == booking.getPlanType() && ObjectUtil.isNotEmpty(booking.getBenefitId())) {
            if (ObjectUtil.isNull(employeeAssistanceProgramBenefitId)) {
                /**
                 * todo 不明确找哪个表 - 先留着 tbl_Aia_Corporate = tbl_plan
                 * SELECT	@medical_service_exclusion_list_id = tbl_Aia_Corporate.medical_service_exclusion_list
                 * 				FROM tbl_Booking
                 * 				JOIN tbl_Aia_Corporate ON tbl_Booking.aia_corporate_id = tbl_Aia_Corporate.aia_corporate_id
                 * 				WHERE booking_id = @PRM_booking_id
                 */
            }
        } else if (2 == booking.getPlanType() && ObjectUtil.isNotEmpty(booking.getBenefitId())) {
            //Direct corporate
            if (ObjectUtil.isNull(employeeAssistanceProgramBenefitId)) {
                /**
                 * todo 不明确找哪个表
                 * SELECT	@medical_service_exclusion_list_id = medical_service_exclusion_list
                 * 				FROM tbl_Direct_Corporate_Benefit
                 * 				WHERE direct_corporate_benefit_id = @direct_corporate_benefit_id
                 */
            }
        } else if (6 == booking.getPlanType() && ObjectUtil.isNotEmpty(booking.getBenefitId())) {
            //ELSE IF(@cdmp_program_benefit_id IS NOT NULL) -- Direct corporate --
            /**
             * todo 不明确表
             * SELECT	@medical_service_inclusion_list_id = medical_service_exclusion_list
             * 			FROM tbl_Cdmp_Program_Benefit
             * 			WHERE cdmp_program_benefit_id = @cdmp_program_benefit_id
             */
        } else if (4 == booking.getPlanType() && ObjectUtil.isNotEmpty(booking.getBenefitId())) {
            //Subscription package benefit
            /**
             * todo 不明确表
             * SELECT	@medical_service_exclusion_list_id = medical_service_exclusion_list_id
             * 			FROM tbl_Subscription_Package_Benefit
             * 			WHERE subscription_package_benefit_id = @subscription_package_benefit_id
             */
        }
        /**
         * 上方if还有个else if 但是这个表已经不存在了，所以不写
         * ELSE IF (@member_ltria_corporate_id IS NOT NULL) -- LTRIA Corporate --
         * 		BEGIN
         * 			SELECT	@medical_service_exclusion_list_id = tbl_Ltria_Corporate.medical_service_exclusion_list
         * 			FROM tbl_Booking
         * 			JOIN tbl_Ltria_Corporate ON tbl_Booking.ltria_corporate_id = tbl_Ltria_Corporate.ltria_corporate_id
         * 			WHERE booking_id = @PRM_booking_id
         */
        //Service package
        String bookingId = booking.getId();
        tblBookingPackageMapper.updateDelFlagTrueByBookingIdAndByType(bookingId, "Service");
        tblBookingMedicalServiceMapper.updateDelFlagTrueByBookingId(booking.getId());
        //检查医疗服务
        if (ObjectUtil.isNotEmpty(dto.getMedical_service_list())) {
            //用于接收下方的sql查询结果
            List<MedicalServiceFindUnionAll> unionAlls = new ArrayList<>();
            for (EndCallDto.MedicalServiceList medicalServiceList : dto.getMedical_service_list()) {
                //Service package 检查包装id
                if (ObjectUtil.isNotEmpty(medicalServiceList.getPackage_id())) {
                    //存在就查询
                    PackageEndCallOfflineBooking packageEndCallOfflineBooking =
                            tblPackageMapper.findEndCallOfflineBookingByPackageId(medicalServiceList.getPackage_id(),
                                    medicalServiceExclusionListId,
                                    medicalServiceInclusionListId);
                    //查出来了就保存 - 这一步是后面的操作 - 提在前面可以节省一些循环
                    if (ObjectUtil.isNotEmpty(packageEndCallOfflineBooking)) {
                        TblBookingPackage bookingPackage = new TblBookingPackage();
                        bookingPackage.setBookingId(bookingId);
                        bookingPackage.setPackageId(medicalServiceList.getPackage_id());
                        bookingPackage.setQuantity(packageEndCallOfflineBooking.getQuantity());
                        bookingPackage.setShow(packageEndCallOfflineBooking.getIsShow());
                        bookingPackage.setServiceType(Kit.isInteger(medicalServiceList.getService_type()) ? Integer.valueOf(medicalServiceList.getService_type()) : null);
                        bookingPackage.setInExclusionList(packageEndCallOfflineBooking.getIsInExclusionList());
                        bookingPackage.setTestPeriod(medicalServiceList.getTest_period() != null ? medicalServiceList.getTest_period() : null);
                        bookingPackage.setTestPeriodUnit(medicalServiceList.getTest_period_unit() != null ? medicalServiceList.getTest_period_unit() : null);
                        bookingPackage.setTempIndex(Kit.isInteger(medicalServiceList.getTemp_index()) ? Integer.valueOf(medicalServiceList.getTemp_index()) : null);
                        tblBookingPackageMapper.insertSelective(bookingPackage);
                    }
                }
                List<MedicalServiceFindUnionAll> unionAll = tblMedicalServiceMapper.findEndCallOfflineBookingUnionAll(medicalServiceList,
                        medicalServiceExclusionListId,
                        medicalServiceInclusionListId);
                //组装结果
                if (ObjectUtil.isNotEmpty(unionAll)) {
                    unionAll.forEach(item -> {
                        item.setServiceType(medicalServiceList.getService_type());
                        item.setTempIndex(medicalServiceList.getTemp_index());
                        item.setTestPeriod(medicalServiceList.getTest_period());
                        item.setTestPeriodUnit(medicalServiceList.getTest_period_unit());
                        item.setPackageId(medicalServiceList.getPackage_id() != null ? medicalServiceList.getPackage_id() : null);
                    });
                    //加入集合 - 下方循环保存
                    unionAlls.addAll(unionAll);
                }
            }
            //查询完成之后开始准备保存bookingMedicalService
            unionAlls.forEach(item -> {
                TblBookingMedicalService bookingMedicalService = new TblBookingMedicalService();
                bookingMedicalService.setBookingId(bookingId);
                bookingMedicalService.setMedicalServiceId(item.getMedicalServiceId());
                bookingMedicalService.setPrice(item.getPrice());
                //original_price字段已经不存在，所以不存
                bookingMedicalService.setPackageId(item.getPackageId() != null ? item.getPackageId() : null);
                bookingMedicalService.setShow(item.getIsShow());
                bookingMedicalService.setServiceType(Kit.isInteger(item.getServiceType()) ? Integer.valueOf(item.getServiceType()) : 1);
                bookingMedicalService.setInExclusionList(item.getIsInExclusionList());
                bookingMedicalService.setTestPeriod(item.getTestPeriod() != null ? item.getTestPeriod() : null);
                bookingMedicalService.setTestPeriodUnit(item.getTestPeriodUnit() != null ? item.getTestPeriodUnit() : null);
                bookingMedicalService.setTempIndex(Kit.isInteger(item.getTempIndex()) ? Integer.valueOf(item.getTempIndex()) : 0);
                tblBookingMedicalServiceMapper.insertSelective(bookingMedicalService);
            });
        }

        //Update booking_package_id when repeat WC-2533
        tblBookingMedicalServiceMapper.updateBookingPackageIdByBookingId(bookingId);

        //Update Diagnosis
        if (ObjectUtil.isNotEmpty(dto.getDiagnosis())) {
            //存在才操作
            //筛选出name不为null然后根据name前后去除空格 - 然后根据name去重 - 最后将新值set进dto中
            dto.setDiagnosis(dto.getDiagnosis().stream()
                    .filter(diagnosis -> diagnosis.getName() != null)
                    .map(diagnosis -> {
                        diagnosis.setName(diagnosis.getName().trim());
                        return diagnosis;
                    })
                    .distinct()
                    .collect(Collectors.toList()));
            //软删除问诊的诊断
            tblBookingDiagnosisMapper.updateDelFlagTrueByBookingId(bookingId);
            //循环准备添加product
            dto.getDiagnosis().forEach(item -> {
                //先查询 - 不存在才添加
                List<TblDiagnosis> diagnosisList = tblDiagnosisMapper.findCheckByNameAndByGroupId(item.getName(), doctor.getGroupId());
                if (ObjectUtil.isEmpty(diagnosisList)) {
                    //准备添加
                    TblDiagnosis diagnosis = new TblDiagnosis();
                    diagnosis.setName(item.getName());
                    diagnosis.setStatus(1);
                    diagnosis.setDispOrder(tblDiagnosisMapper.findMaxDispOrderAddOneByGroupId(doctor.getGroupId()));
                    diagnosis.setCountryOfResidence(doctor.getCountryOfResidence());
                    diagnosis.setCode(item.getCode() != null ? item.getCode() : null);
                    tblDiagnosisMapper.insertSelective(diagnosis);
                    //将添加之后的id设置进dto内
                    item.setId(diagnosis.getId());
                }
                //添加问诊的诊断数据
                TblBookingDiagnosis bookingDiagnosis = new TblBookingDiagnosis();
                bookingDiagnosis.setBookingId(bookingId);
                bookingDiagnosis.setDiagnosisId(item.getId());
                bookingDiagnosis.setInExclusionList(item.getIs_in_exclusion_list() ? 1 : 0);
                tblBookingDiagnosisMapper.insertSelective(bookingDiagnosis);
            });
            /**
             * todo 这里差一个
             * --WC-3272
             * 	DECLARE @json NVARCHAR(MAX)
             * 	SET @json=(SELECT * FROM @PRM_diagnosis
             * 		FOR JSON AUTO)
             * 	EXEC API_Doctor_UpdateDiagnosisLog @PRM_booking_id,@json,0
             */
        }

        //Add Recommend
        if (ObjectUtil.isNotEmpty(dto.getRecommends())) {
            //todo 这里少了一个函数调用,示例json中这个值是空的，不知道怎么做 Exec [dbo].[API_DoctorAddRecommend] @PRM_booking_id, @PRM_recommends
            /**
             * 具体函数为：
             * ALTER Procedure [dbo].[API_DoctorAddRecommend]
             * (
             *        @PRM_booking_id UniqueIdentifier,
             *    @PRM_recommends Type_Recommend ReadOnly
             * )
             * As
             * Begin
             *
             * 	Delete tbl_Booking_Recommend where booking_id=@PRM_booking_id
             * 	INSERT INTO tbl_Booking_Recommend (booking_recommend_id,booking_id, title,frequency,order_by)
             * 	SELECT booking_recommend_id,
             * 	       booking_id,
             * 		   title,frequency,
             * 		   case when title LIKE '%blood sugar%' then 2 else 1 end AS rownum
             * 	FROM @PRM_recommends
             *
             * End
             */
        }

        //WC-4173
        TblBookingFee bookingFee = tblBookingFeeMapper.findByBookingId(bookingId);
        SysCode code = SysCodeUtil.getCode("Tax_Rate", "2");
        //下方判断中	bookingFee.getTaxRateType() != 1 的1来自于：Select @config_tax_rate_type = [dbo].[fn_GetConfig_TaxRateType]() 对方函数写死的1
        if (ObjectUtil.isNotEmpty(bookingFee.getTaxRate()) &&
                (ObjectUtil.isNotEmpty(code.getValue()) && Kit.isInteger(code.getValue()) &&
                        !Objects.equals(bookingFee.getTaxRate(), BigDecimal.valueOf(Long.parseLong(code.getValue())))
                        && bookingFee.getTaxRateType() != 1) && 1 == booking.getIsAppointment()) {
            bookingFee.setTaxRate(BigDecimal.valueOf(Long.parseLong(code.getValue())));
            bookingFee.setTaxRateType(1);
            tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);
        }
        //检查深度链接
        String deeplinkId = null;
        TblDeeplink deeplink = null;
        if (ObjectUtil.isNotEmpty(booking.getDeeplinkId())) {
            if (ObjectUtil.isEmpty(booking.getSpecialisationId()) || booking.getSpecialisationId().equals("0")) {
                deeplink = tblDeeplinkMapper.selectByPrimaryKey(booking.getDeeplinkId());
                if (ObjectUtil.isNotEmpty(deeplink) && 1 == deeplink.getStatus()) {
                    deeplinkId = deeplink.getId();
                }
            }
        }
        TblBookingConsultation bookingConsultation = tblBookingConsultationMapper.findByBookingId(bookingId);
        if (5 == booking.getConsultationMode() && ObjectUtil.isNotEmpty(dto.getConsultationCharges())
                && ObjectUtil.isNotEmpty(dto.getPracticeCharge())) {
            dto.setConsultationCharges(dto.getConsultationCharges().setScale(2, BigDecimal.ROUND_DOWN));
            //实际上后面还有个+ @medical_service_fee,但是对方定义@medical_service_fee字段为0，所以没必要。
            //SET @medical_service_fee = 0,SET @medical_service_fee = ISNULL(@medical_service_fee, 0)
            BigDecimal standardFee = dto.getConsultationCharges().add(dto.getPracticeCharge());
            BigDecimal totalAmount = fnGetAmountAfterTaxRate(standardFee, bookingFee.getTaxRate(), 1);
            bookingFee.setIsSurcharge(0);
            bookingFee.setPracticeFee(dto.getPracticeCharge());
            bookingFee.setTotalAmount(totalAmount);
            bookingFee.setStandardFee(standardFee);
            bookingFee.setConsultationFee(dto.getConsultationCharges());
            bookingFee.setMedicalServiceFee(new BigDecimal(0));
            tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);

            bookingConsultation.setAmount(dto.getConsultationCharges());
            tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
        }

        List<TblBookingPrescription> bookingPrescriptions = tblBookingPrescriptionMapper.findByBookingId(bookingId);
        //dispensed inventory for offline consult
        if (5 == booking.getConsultationMode()) {
            if (ObjectUtil.isNotEmpty(bookingPrescriptions)) {
                bookingPrescriptions.forEach(item -> {
                    if (ObjectUtil.isNotEmpty(item.getProductId())) {
                        TblProduct product = tblProductMapper.selectByPrimaryKey(item.getProductId());
                        if (ObjectUtil.isNotEmpty(product)) {
//                            item.setStatus(product.getPartner() == 1 ? 0 : 3);
                            Example example = new Example(TblBookingOrder.class);
                            Example.Criteria criteria = example.createCriteria();
                            criteria.andEqualTo("prescriptionId",item.getId()).andEqualTo("delFlag",0);
                            TblBookingOrder condition = new TblBookingOrder();
                            condition.setStatus(product.getPartner() == 1 ? 0 : 3);
                            tblBookingOrderMapper.updateByExample(condition,example);
                            item.setShow(1);
                            tblBookingPrescriptionMapper.updateByPrimaryKeySelective(item);
                        } else {
                            tblBookingPrescriptionMapper.deleteByPrimaryKey(item.getBookingId());
                        }
                    } else {
                        tblBookingPrescriptionMapper.deleteByPrimaryKey(item.getBookingId());
                    }
                });
            }
            //根据bookingId修改status=3 并且修改 show = 1
            tblBookingMedicalServiceMapper.updateStatusThreeAndShowTrueByBookingId(bookingId);
            tblBookingPackageMapper.updateStatusThreeAndShowTrueByBookingId(bookingId);

            //Fix wc-881: Offline consult medication should not be on hold if not purchased
            tblInventoryFeedMapper.updateDelFlagTrueByBookingAndByTypeTwoAndByProductIdIn(bookingId);

            //todo 这里还有个函数调用，先跳过，这个函数有500行，Exec [dbo].[sp_UpdateMedicationInventory] @PRM_booking_id, -2
        }

        //Update booking_company_id,MDClinics_id for access control
        Boolean needToModify = false;
        if (ObjectUtil.isEmpty(booking.getCompanyId())) {
            booking.setCompanyId(doctor.getCompanyId());
            needToModify = true;
        }
        if (ObjectUtil.isEmpty(booking.getClinicId())) {
            booking.setClinicId(doctor.getCurrentClinicId());
            needToModify = true;
        }
        if (needToModify) {
            tblBookingMapper.updateByPrimaryKeySelective(booking);
        }

        //START: caculate total cost prescription
        Integer totalNumberOfPrescribedMedication = tblBookingPrescriptionMapper.findCountByBookingId(bookingId);
        bookingFee.setPrescriptionFee(new BigDecimal(0));
        tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);
        TblDoctorConsultFee doctorConsultFee = tblDoctorConsultFeeMapper.selectByPrimaryKey(dto.getConsult_fee_id());
        if (5 != booking.getConsultationMode() && 2 == booking.getConsultType() && ObjectUtil.isNotEmpty(dto.getConsult_fee_id())) {
            if (ObjectUtil.isNotEmpty(doctorConsultFee)) {
                if (!booking.getPatientCountryOfResidence().equals("106")) {
                    bookingFee.setStandardFee(new BigDecimal(doctorConsultFee.getTypeRate()).setScale(2, BigDecimal.ROUND_DOWN));
                    bookingFee.setConsultationFee(new BigDecimal(doctorConsultFee.getTypeRate()).setScale(2, BigDecimal.ROUND_DOWN));
                    bookingFee.setTotalAmount(fnGetAmountAfterTaxRateOfBooking(new BigDecimal(doctorConsultFee.getTypeRate())
                            .setScale(2, BigDecimal.ROUND_DOWN), bookingFee));
                    bookingFee.setStandardFee(new BigDecimal(0));
                    tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);
                }
                bookingConsultation.setAmount(new BigDecimal(doctorConsultFee.getTypeRate()).setScale(2, BigDecimal.ROUND_DOWN));
                tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
            }
        }

        if ((5 != booking.getConsultationMode() && dto.getAia_member() &&
                (1 == booking.getPlanType() || 2 == booking.getPlanType() || 4 == booking.getPlanType() ||
                        6 == booking.getPlanType() && ObjectUtil.isEmpty(booking.getBenefitId()))) || (
                ObjectUtil.isNotEmpty(booking.getDeeplinkId()) && (1 == booking.getPlanType() ||
                        2 == booking.getPlanType() || 4 == booking.getPlanType() || 6 == booking.getPlanType()
                        && ObjectUtil.isEmpty(booking.getBenefitId())))) {

            dto.setConsultationCharges(fnAiaGetConsultCharge(bookingFee, booking));
            if (!dto.getAia_member() || ObjectUtil.isNotEmpty(booking.getDeeplinkId())) {
                dto.setConsultationCharges(deeplink.getConsultFeeNormalHours() != null ? deeplink.getConsultFeeNormalHours() : new BigDecimal(0));
            } else {
                booking.setDeeplinkId(null);
                tblBookingMapper.updateDeeplinkIdNullByBookingId(bookingId);
            }

            if (bookingFee.getStandardFee().compareTo(BigDecimal.ZERO) > 0) {
                if (!dto.getAia_member() && ObjectUtil.isNotEmpty(booking.getDeeplinkId())) {
                    dto.setConsultationCharges(deeplink.getConsultFeeAfterHours());
                } else {
                    booking.setDeeplinkId(null);
                    tblBookingMapper.updateDeeplinkIdNullByBookingId(bookingId);
                }
            }

            //函数431行
            if (bookingFee.getStandardFee().compareTo(BigDecimal.ZERO) != 0) {
                bookingFee.setStandardFee(dto.getConsultationCharges());
                bookingFee.setPracticeFee(dto.getPracticeCharge());
                //实际上后面还有个+ @medical_service_fee,但是对方定义@medical_service_fee字段为0，所以没必要。
                //SET @medical_service_fee = 0,SET @medical_service_fee = ISNULL(@medical_service_fee, 0)
                bookingFee.setTotalAmount(fnGetAmountAfterTaxRate(dto.getConsultationCharges().add(dto.getPracticeCharge()),
                        booking.getPatientCountryOfResidence().equals("106") ? new BigDecimal("0.0") : new BigDecimal(code.getValue()), 1));
                bookingFee.setStandardFee(dto.getConsultationCharges().add(dto.getPracticeCharge()));
                bookingFee.setConsultationFee(new BigDecimal(0));
                tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);
                bookingConsultation.setAmount(new BigDecimal(0));
                tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
            } else {
                bookingFee.setStandardFee(new BigDecimal(0));
                bookingFee.setPracticeFee(dto.getPracticeCharge());
                bookingFee.setTotalAmount(fnGetAmountAfterTaxRate(dto.getConsultationCharges().add(dto.getPracticeCharge()),
                        booking.getPatientCountryOfResidence().equals("106") ? new BigDecimal("0.0") : new BigDecimal(code.getValue()), 1));
                bookingFee.setStandardFee(dto.getConsultationCharges().add(dto.getPracticeCharge()));
                bookingFee.setConsultationFee(dto.getConsultationCharges());
                tblBookingFeeMapper.updateByPrimaryKeySelective(bookingFee);
                bookingConsultation.setAmount(dto.getConsultationCharges());
                tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
            }
        } else {
            //Update tbl_Booking set Deeplink_Id=null,update_on = [dbo].[fn_Add_Hours]()  Where booking_id = @booking_id
            booking.setDeeplinkId(null);
            tblBookingMapper.updateDeeplinkIdNullByBookingId(bookingId);
        }

        String patientReceiptLabel = null;
        //WC-1925
        if (ObjectUtil.isEmpty(dto.getConsult_fee_id())) {
            dto.setConsult_fee_id(null);
            if (ObjectUtil.isNotEmpty(doctorConsultFee) && ObjectUtil.isNotEmpty(doctorConsultFee.getPatientReceiptLabel())) {
                patientReceiptLabel = doctorConsultFee.getPatientReceiptLabel();
            }
        }

        BigDecimal bookingWaiveMedicalServiceFee = new BigDecimal(0);
        //函数463行
//        if (dto.getWaive_medical_service()) {
//            bookingWaiveMedicalServiceFee = new BigDecimal(0);
//        }

        //Caculate 'waive consultation fee'
        BigDecimal bookingWaiveConsultationFee = new BigDecimal(0);
        if (dto.getWaive_consultation_fee()) {
            bookingWaiveConsultationFee = bookingFee.getConsultationFee()
                    .add(ObjectUtil.isNotEmpty(bookingFee.getStandardFee()) ? bookingFee.getStandardFee() : new BigDecimal(0));
        }

        //Caculate 'waive consultation fee'
        BigDecimal bookingWaiveMedicationFee = new BigDecimal(0);
        if (dto.getWaive_medication_fee()) {
            bookingWaiveMedicationFee = ObjectUtil.isNotEmpty(bookingFee.getPrescriptionFee()) ? bookingFee.getPrescriptionFee() : new BigDecimal(0);
        }

        //Caculate 'waive consultation fee'
        BigDecimal bookingWaivePracticeFee = new BigDecimal(0);
        if (dto.getWaive_practice_fee()) {
            bookingWaivePracticeFee = ObjectUtil.isNotEmpty(bookingFee.getPracticeFee()) ? bookingFee.getPracticeFee() : new BigDecimal(0);
        }

        //total waive fee
        BigDecimal totalWaiveFee = bookingWaiveConsultationFee.add(bookingWaiveMedicationFee).add(bookingWaivePracticeFee).add(bookingWaiveMedicalServiceFee);

        //service type fee
        Integer countHomeBase = tblBookingMedicalServiceMapper.findCountByBookingIdAndByServiceType(bookingId, 1)
                + tblBookingPackageMapper.findCountByBookingIdAndByServiceType(bookingId, 1);

        //这个表经过查看 - 对方表里就三条数据 - 两个type为0 - 一个type为1 所以应该type为1只有一条数据，而且数据还是几年前的。
        TblMedicalServiceType medicalServiceType = tblMedicalServiceTypeMapper.findByType(1);//这个数据只是用于代码内操作 - 不进行数据库修改
        BigDecimal serviceTypeFee = null;
        String serviceTypeName = null;
        if (ObjectUtil.isNotEmpty(medicalServiceType)) {
            serviceTypeFee = medicalServiceType.getFee();
            serviceTypeName = medicalServiceType.getName();
        }
        Integer showInBill = 0;
        //497行
        if (countHomeBase > 0) {
            if ((1 == booking.getPlanType() || 2 == booking.getPlanType() || 4 == booking.getPlanType())
                    && ObjectUtil.isNotEmpty(booking.getBenefitId())) {
                if (1 == booking.getPlanType()) {
                    showInBill = 1;
                }
                TblPlanBenefit planBenefit = tblPlanBenefitMapper.findById(booking.getBenefitId());
                if (ObjectUtil.isNotEmpty(planBenefit.getHomeBasedFee())) {
                    serviceTypeFee = planBenefit.getHomeBasedFee();
                }
            }
        } else {
            //Select @service_type_fee=0,@show_in_bill=1, @service_type_name=''
            serviceTypeFee = new BigDecimal(0);
            showInBill = 0;
            serviceTypeName = "";
        }

        TblBookingSummary bookingSummary = tblBookingSummaryMapper.findByBookingId(bookingId);

        //529行 update booking
        bookingFee.setWaiveConsultationFeeFlag(dto.getWaive_consultation_fee() ? 1 : 0);
        bookingFee.setWaiveDeliveryFeeFlag(dto.getWaive_delivery_fee() ? 1 : 0);
        bookingSummary.setNeedMedicalLetter(dto.getNeed_medical_letter() ? 1 : 0);
        bookingSummary.setNeedMedicalCertification(dto.getNeed_medical_certification() ? 1 : 0);
        bookingFee.setWaiveMedicationFee(bookingWaiveMedicationFee);
        bookingFee.setServiceTypeFee(serviceTypeFee);
        bookingFee.setShowInBill(showInBill);
        bookingFee.setServiceTypeName(serviceTypeName);
        bookingFee.setWaiveConsultationFee(dto.getWaive_consultation_fee() ? bookingFee.getConsultationFee()
                .add(ObjectUtil.isNotEmpty(bookingFee.getStandardFee()) ? bookingFee.getStandardFee() : new BigDecimal(0)) : new BigDecimal(0));
        BigDecimal bookingStandardFee = bookingFee.getStandardFee().subtract(totalWaiveFee);
        bookingFee.setStandardFee(bookingStandardFee);
        bookingFee.setTotalAmount(fnGetAmountAfterTaxRate(bookingStandardFee, bookingFee.getTotalAmount(), 1));
        bookingFee.setWaiveConsultationReason(dto.getReason() != null ? dto.getReason() : null);
        bookingFee.setWaiveConsultationFeeType(dto.getWaive_consultation_fee_type());
        bookingFee.setWaiveDeliveryReason(dto.getWaive_delivery_reason() != null ? dto.getWaive_delivery_reason() : null);
        //update is_waived, reason for waive_medication
        bookingFee.setWaiveMedicationFeeFlag(dto.getWaive_medication_fee() ? 1 : 0);
        bookingFee.setWaiveMedicationReason(dto.getWaive_medication_reason() != null ? dto.getWaive_medication_reason() : null);
        //update is_waived, reason for waive_practice
        bookingFee.setWaivePracticeFeeFlag(dto.getWaive_practice_fee() ? 1 : 0);
        bookingFee.setWaivePracticeReason(dto.getWaive_practice_reason());
        TblBookingPatientAddress bookingPatientAddress = tblBookingPatientAddressMapper.findByBookingId(bookingId);
        bookingPatientAddress.setPatientLocationClinicId(dto.getBooking_clinic_id() != null ? dto.getBooking_clinic_id() : null);
        String address = tblClinicMapper.findAddressById(dto.getBooking_clinic_id());
        bookingPatientAddress.setDetailAddress(address != null ? address : "");
        bookingFee.setWaiveMedicalServiceFlag(dto.getWaive_medical_service() ? 1 : 0);
        bookingFee.setWaiveMedicalServiceReason(dto.getWaive_medical_service_reason() != null ? dto.getWaive_medical_service_reason() : null);
        bookingFee.setWaiveMedicalServiceFee(bookingWaiveMedicalServiceFee);
        bookingFee.setPaymentAt(Kit.getUtcDate());
        //location_address = @PRM_location_address,对方还set了这个值，但是查看文档，上方的patient_location和这个location_address指向的是同一个字段：tbl_booking_patient_address的detail_address
        bookingPatientAddress.setUnitNumber(dto.getLocation_unit_number() != null ? dto.getLocation_unit_number() : null);
        bookingPatientAddress.setPostalCode(dto.getLocation_postal_code() != null ? dto.getLocation_postal_code() : null);
        bookingPatientAddress.setCountry(dto.getLocation_country() != null ? dto.getLocation_country() : null);
        bookingPatientAddress.setState(dto.getLocation_state() != null ? dto.getLocation_state() : null);
        bookingFee.setConsultationFeeLabel(patientReceiptLabel);
        bookingSummary.setPregnant(dto.getPregnant() ? 1 : 0);
        bookingSummary.setG6pd(dto.getG6dp() ? 1 : 0);
        booking.setDoctorManual(ObjectUtil.isNotEmpty(dto.getDoctor_manual()) ? dto.getDoctor_manual() ? 1 : 0 : 0);
        bookingSummary.setDiagnosisAllowPatientToClaim(ObjectUtil.isNotEmpty(dto.getDiagnosis_allow_patient_to_claim()) ? dto.getDiagnosis_allow_patient_to_claim() ? 1 : 0 : null);
        //todo 修改数据库 - 先往后看是不是可以最终才修改


        //START: IF PATIENT USEDS CORPORATE PROFILE
        BigDecimal totalAmount = fnGetAmountAfterTaxRateOfBooking(bookingFee.getStandardFee(), bookingFee);
        BigDecimal gts = ObjectUtil.isNotNull(totalAmount.subtract(bookingFee.getStandardFee())) ? totalAmount.subtract(bookingFee.getStandardFee()) : new BigDecimal("0");
        //这个问诊费 抹除也是0  - 不抹除也就不用算
//        sum_cost_consult BigDecimal sumCostConsult = bookingFee.getConsultationFee().add(bookingFee.get)
        //一样 抹除是0  不抹除直接取
//        sum_cost_practice bookingFee.getPracticeFee().subtract()

        //START: AIA CORPORATE
        if (1 ==booking.getPlanType() && ObjectUtil.isNotEmpty(booking.getBenefitId())){
            //todo 函数：API_DoctorEndCallOfflineBooking 的该做629行
        }


        /**
         * todo 最终还需要：
         * System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
         * {
         *     member_bl.SendMessageAndResetQueueNo(model.booking_id, "doctor.endcall", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         * });
         * threadSocket.IsBackground = true;
         * threadSocket.Start();
         * booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(model), "EndCallOfflineBooking").ConfigureAwait(false);
         */
        return ResponseResultApp.success();
    }


    /**
     * 函数fn_Aia_Get_Consult_Charge 内部包含 fn_FormatRoundNumber
     *
     * @param bookingFee
     * @param booking
     * @return
     */
    public BigDecimal fnAiaGetConsultCharge(TblBookingFee bookingFee, TblBooking booking) {
        //判断不为0
        if (bookingFee.getStandardFee().compareTo(BigDecimal.ZERO) != 0) {
            if (booking.getPatientCountryOfResidence().equals("106")) {
                String name = "";
                if (booking.getDoctorCountryOfResidence().equals("106")) {
                    name = "Indonesia_AIA_Identifier_Consult_Fee_After_Hours";
                } else {
                    name = "Singapore_AIA_Identifier_Consult_Fee_After_Hours";
                }
                return new BigDecimal(SysCodeUtil.getCode(name, "8").getValue()).setScale(2, BigDecimal.ROUND_DOWN);
            } else {
                return new BigDecimal(sysCodeMapper.selectByPrimaryKey("75").getValue()).setScale(2, BigDecimal.ROUND_DOWN);
            }
        } else {
            if (booking.getPatientCountryOfResidence().equals("106")) {
                String name = "";
                if (booking.getDoctorCountryOfResidence().equals("106")) {
                    name = "Indonesia_AIA_Identifier_Consult_Fee_Normal_Hours";
                } else {
                    name = "Singapore_AIA_Identifier_Consult_Fee_Normal_Hours";
                }
                return new BigDecimal(SysCodeUtil.getCode(name, "8").getValue()).setScale(2, BigDecimal.ROUND_DOWN);
            } else {
                return new BigDecimal(sysCodeMapper.selectByPrimaryKey("74").getValue()).setScale(2, BigDecimal.ROUND_DOWN);
            }
        }
    }

    /**
     * 函数：fn_Get_AmountAfterTaxRateOfBooking
     *
     * @param amount
     * @param bookingFee
     * @return
     */
    public BigDecimal fnGetAmountAfterTaxRateOfBooking(BigDecimal amount, TblBookingFee bookingFee) {
        if (ObjectUtil.isNull(amount)) {
            amount = new BigDecimal(0);
        }
        return fnGetAmountAfterTaxRate(amount,
                bookingFee.getTaxRate() != null ? bookingFee.getTaxRate() : new BigDecimal(0),
                bookingFee.getTaxRateType() != null ? bookingFee.getTaxRateType() : 0);
    }


    /**
     * 根据不同type做不同计算 - 这里主要用于EndCallOfflineBooking接口
     * 对方调用接口为：fn_Get_AmountAfterTaxRate - 如有相同的可以调用这个方法
     *
     * @param amount
     * @param taxAmount
     * @param taxType
     * @return
     */
    public BigDecimal fnGetAmountAfterTaxRate(BigDecimal amount, BigDecimal taxAmount, Integer taxType) {
        BigDecimal returnBig = new BigDecimal(0);
        if (1 == taxType) {
            returnBig = (amount.multiply(taxAmount)).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_DOWN);
            return returnBig.add(amount).setScale(2, BigDecimal.ROUND_DOWN);
        } else {
            return amount.add(taxAmount).setScale(2, BigDecimal.ROUND_DOWN);
        }
    }


    public void spCheckConsultAndLabResult(TblMember member, TblBooking booking, TblDoctor doctor) {
        if (ObjectUtil.isNotEmpty(member)) {
            if (ObjectUtil.isNotEmpty(member.getWeight()) || ObjectUtil.isNotEmpty(member.getHeight())) {
//                tblBookingLogMapper
            }
        } else {

        }
    }

}
